diff --git a/MAINTAINERS b/MAINTAINERS index 9529c9484c..b9aa878b84 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -84,14 +84,10 @@ M: Paolo Bonzini M: Peter Crosthwaite M: Richard Henderson S: Maintained -F: cpu-exec.c -F: cpu-exec-common.c F: cpus.c -F: cputlb.c F: exec.c F: softmmu_template.h -F: translate-all.* -F: translate-common.c +F: accel/tcg/ F: include/exec/cpu*.h F: include/exec/exec-all.h F: include/exec/helper*.h @@ -277,8 +273,8 @@ Overall M: Paolo Bonzini L: kvm@vger.kernel.org S: Supported -F: kvm-* F: */kvm.* +F: accel/kvm/ F: include/sysemu/kvm*.h ARM @@ -327,7 +323,6 @@ M: Stefano Stabellini M: Anthony Perard L: xen-devel@lists.xenproject.org S: Supported -F: xen-* F: */xen* F: hw/9pfs/xen-9p-backend.c F: hw/char/xen_console.c @@ -1160,6 +1155,13 @@ F: docs/specs/vmgenid.txt F: tests/vmgenid-test.c F: stubs/vmgenid.c +Unimplemented device +M: Peter Maydell +R: Philippe Mathieu-Daudé +S: Maintained +F: include/hw/misc/unimp.h +F: hw/misc/unimp.c + Subsystems ---------- Audio @@ -1650,7 +1652,7 @@ TCI target M: Stefan Weil S: Maintained F: tcg/tci/ -F: tci.c +F: tcg/tci.c F: disas/tci.c Block drivers diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 0caf80db75..4e1831cbb9 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1851,11 +1851,6 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) tb_lock(); - if (!tcg_enabled()) { - cpu_fprintf(f, "TCG not enabled\n"); - return; - } - target_code_size = 0; max_target_code_size = 0; cross_page = 0; diff --git a/backends/rng-egd.c b/backends/rng-egd.c index e7ce2cac80..d2b9ce6cbf 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -106,7 +106,7 @@ static void rng_egd_opened(RngBackend *b, Error **errp) /* FIXME we should resubmit pending requests when the CDS reconnects. */ qemu_chr_fe_set_handlers(&s->chr, rng_egd_chr_can_read, - rng_egd_chr_read, NULL, s, NULL, true); + rng_egd_chr_read, NULL, NULL, s, NULL, true); } static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) diff --git a/block/nbd-client.c b/block/nbd-client.c index 208f907095..25dd28406b 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -242,7 +242,7 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, ssize_t ret; if (flags & BDRV_REQ_FUA) { - assert(client->nbdflags & NBD_FLAG_SEND_FUA); + assert(client->info.flags & NBD_FLAG_SEND_FUA); request.flags |= NBD_CMD_FLAG_FUA; } @@ -270,12 +270,12 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, }; NBDReply reply; - if (!(client->nbdflags & NBD_FLAG_SEND_WRITE_ZEROES)) { + if (!(client->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) { return -ENOTSUP; } if (flags & BDRV_REQ_FUA) { - assert(client->nbdflags & NBD_FLAG_SEND_FUA); + assert(client->info.flags & NBD_FLAG_SEND_FUA); request.flags |= NBD_CMD_FLAG_FUA; } if (!(flags & BDRV_REQ_MAY_UNMAP)) { @@ -299,7 +299,7 @@ int nbd_client_co_flush(BlockDriverState *bs) NBDReply reply; ssize_t ret; - if (!(client->nbdflags & NBD_FLAG_SEND_FLUSH)) { + if (!(client->info.flags & NBD_FLAG_SEND_FLUSH)) { return 0; } @@ -327,7 +327,7 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) NBDReply reply; ssize_t ret; - if (!(client->nbdflags & NBD_FLAG_SEND_TRIM)) { + if (!(client->info.flags & NBD_FLAG_SEND_TRIM)) { return 0; } @@ -384,22 +384,24 @@ int nbd_client_init(BlockDriverState *bs, logout("session init %s\n", export); qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); + client->info.request_sizes = true; ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, - &client->nbdflags, tlscreds, hostname, - &client->ioc, - &client->size, errp); + &client->ioc, &client->info, errp); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); return ret; } - if (client->nbdflags & NBD_FLAG_SEND_FUA) { + if (client->info.flags & NBD_FLAG_SEND_FUA) { bs->supported_write_flags = BDRV_REQ_FUA; bs->supported_zero_flags |= BDRV_REQ_FUA; } - if (client->nbdflags & NBD_FLAG_SEND_WRITE_ZEROES) { + if (client->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) { bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; } + if (client->info.min_block > bs->bl.request_alignment) { + bs->bl.request_alignment = client->info.min_block; + } qemu_co_mutex_init(&client->send_mutex); qemu_co_queue_init(&client->free_sema); diff --git a/block/nbd-client.h b/block/nbd-client.h index 49636bc621..df80771357 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -20,8 +20,7 @@ typedef struct NBDClientSession { QIOChannelSocket *sioc; /* The master data channel */ QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ - uint16_t nbdflags; - off_t size; + NBDExportInfo info; CoMutex send_mutex; CoQueue free_sema; diff --git a/block/nbd.c b/block/nbd.c index d529305330..a50d24b50a 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -472,9 +472,17 @@ static int nbd_co_flush(BlockDriverState *bs) static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) { - bs->bl.max_pdiscard = NBD_MAX_BUFFER_SIZE; - bs->bl.max_pwrite_zeroes = NBD_MAX_BUFFER_SIZE; - bs->bl.max_transfer = NBD_MAX_BUFFER_SIZE; + NBDClientSession *s = nbd_get_client_session(bs); + uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block); + + bs->bl.max_pdiscard = max; + bs->bl.max_pwrite_zeroes = max; + bs->bl.max_transfer = max; + + if (s->info.opt_block && + s->info.opt_block > bs->bl.opt_transfer) { + bs->bl.opt_transfer = s->info.opt_block; + } } static void nbd_close(BlockDriverState *bs) @@ -492,7 +500,7 @@ static int64_t nbd_getlength(BlockDriverState *bs) { BDRVNBDState *s = bs->opaque; - return s->client.size; + return s->client.info.size; } static void nbd_detach_aio_context(BlockDriverState *bs) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index b550cee0cb..19b2b8fecb 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -85,6 +85,8 @@ struct emulated_sigtable { /* NOTE: we force a big alignment so that the stack stored after is aligned too */ typedef struct TaskState { + pid_t ts_tid; /* tid (or pid) of this task */ + struct TaskState *next; int used; /* non zero if used */ struct image_info *info; diff --git a/chardev/char-fe.c b/chardev/char-fe.c index 3f90f0567c..f3af6ae584 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -179,9 +179,21 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) Chardev *qemu_chr_fe_get_driver(CharBackend *be) { + /* this is unsafe for the users that support chardev hotswap */ + assert(be->chr_be_change == NULL); return be->chr; } +bool qemu_chr_fe_backend_connected(CharBackend *be) +{ + return !!be->chr; +} + +bool qemu_chr_fe_backend_open(CharBackend *be) +{ + return be->chr && be->chr->be_open; +} + bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp) { int tag = 0; @@ -216,7 +228,7 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del) assert(b); if (b->chr) { - qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true); + qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, NULL, true); if (b->chr->be == b) { b->chr->be = NULL; } @@ -235,6 +247,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, IOEventHandler *fd_event, + BackendChangeHandler *be_change, void *opaque, GMainContext *context, bool set_open) @@ -258,6 +271,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, b->chr_can_read = fd_can_read; b->chr_read = fd_read; b->chr_event = fd_event; + b->chr_be_change = be_change; b->opaque = opaque; if (cc->chr_update_read_handler) { cc->chr_update_read_handler(s, context); diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 08570b915e..4cda5e7458 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -278,6 +278,7 @@ void mux_chr_set_handlers(Chardev *chr, GMainContext *context) mux_chr_can_read, mux_chr_read, mux_chr_event, + NULL, chr, context, true); } diff --git a/chardev/char-socket.c b/chardev/char-socket.c index a050a686ea..1ae730a4cb 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -454,7 +454,9 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) return 0; } + qio_channel_set_blocking(s->ioc, true, NULL); size = tcp_chr_recv(chr, (void *) buf, len); + qio_channel_set_blocking(s->ioc, false, NULL); if (size == 0) { /* connection closed */ tcp_chr_disconnect(chr); diff --git a/chardev/char.c b/chardev/char.c index 2b679a2295..c34b44abc9 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -556,17 +556,23 @@ help_string_append(const char *name, void *opaque) g_string_append_printf(str, "\n%s", name); } -Chardev *qemu_chr_new_from_opts(QemuOpts *opts, - Error **errp) +static const char *chardev_alias_translate(const char *name) +{ + int i; + for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { + if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) { + return chardev_alias_table[i].typename; + } + } + return name; +} + +ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) { Error *local_err = NULL; const ChardevClass *cc; - Chardev *chr; - int i; ChardevBackend *backend = NULL; - const char *name = qemu_opt_get(opts, "backend"); - const char *id = qemu_opts_id(opts); - char *bid = NULL; + const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend")); if (name == NULL) { error_setg(errp, "chardev: \"%s\" missing backend", @@ -574,7 +580,40 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, return NULL; } - if (is_help_option(name)) { + cc = char_get_class(name, errp); + if (cc == NULL) { + return NULL; + } + + backend = g_new0(ChardevBackend, 1); + backend->type = CHARDEV_BACKEND_KIND_NULL; + + if (cc->parse) { + cc->parse(opts, backend, &local_err); + if (local_err) { + error_propagate(errp, local_err); + qapi_free_ChardevBackend(backend); + return NULL; + } + } else { + ChardevCommon *ccom = g_new0(ChardevCommon, 1); + qemu_chr_parse_common(opts, ccom); + backend->u.null.data = ccom; /* Any ChardevCommon member would work */ + } + + return backend; +} + +Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) +{ + const ChardevClass *cc; + Chardev *chr = NULL; + ChardevBackend *backend = NULL; + const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend")); + const char *id = qemu_opts_id(opts); + char *bid = NULL; + + if (name && is_help_option(name)) { GString *str = g_string_new(""); chardev_name_foreach(help_string_append, str); @@ -589,38 +628,20 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, return NULL; } - for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { - if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) { - name = chardev_alias_table[i].typename; - break; - } + backend = qemu_chr_parse_opts(opts, errp); + if (backend == NULL) { + return NULL; } cc = char_get_class(name, errp); if (cc == NULL) { - return NULL; + goto out; } - backend = g_new0(ChardevBackend, 1); - backend->type = CHARDEV_BACKEND_KIND_NULL; - if (qemu_opt_get_bool(opts, "mux", 0)) { bid = g_strdup_printf("%s-base", id); } - chr = NULL; - if (cc->parse) { - cc->parse(opts, backend, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto out; - } - } else { - ChardevCommon *ccom = g_new0(ChardevCommon, 1); - qemu_chr_parse_common(opts, ccom); - backend->u.null.data = ccom; /* Any ChardevCommon member would work */ - } - chr = qemu_chardev_new(bid ? bid : id, object_class_get_name(OBJECT_CLASS(cc)), backend, errp); @@ -930,6 +951,89 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, return ret; } +ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, + Error **errp) +{ + CharBackend *be; + const ChardevClass *cc; + Chardev *chr, *chr_new; + bool closed_sent = false; + ChardevReturn *ret; + + chr = qemu_chr_find(id); + if (!chr) { + error_setg(errp, "Chardev '%s' does not exist", id); + return NULL; + } + + if (CHARDEV_IS_MUX(chr)) { + error_setg(errp, "Mux device hotswap not supported yet"); + return NULL; + } + + if (qemu_chr_replay(chr)) { + error_setg(errp, + "Chardev '%s' cannot be changed in record/replay mode", id); + return NULL; + } + + be = chr->be; + if (!be) { + /* easy case */ + object_unparent(OBJECT(chr)); + return qmp_chardev_add(id, backend, errp); + } + + if (!be->chr_be_change) { + error_setg(errp, "Chardev user does not support chardev hotswap"); + return NULL; + } + + cc = char_get_class(ChardevBackendKind_lookup[backend->type], errp); + if (!cc) { + return NULL; + } + + chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), + backend, errp); + if (!chr_new) { + return NULL; + } + chr_new->label = g_strdup(id); + + if (chr->be_open && !chr_new->be_open) { + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); + closed_sent = true; + } + + chr->be = NULL; + qemu_chr_fe_init(be, chr_new, &error_abort); + + if (be->chr_be_change(be->opaque) < 0) { + error_setg(errp, "Chardev '%s' change failed", chr_new->label); + chr_new->be = NULL; + qemu_chr_fe_init(be, chr, &error_abort); + if (closed_sent) { + qemu_chr_be_event(chr, CHR_EVENT_OPENED); + } + object_unref(OBJECT(chr_new)); + return NULL; + } + + object_unparent(OBJECT(chr)); + object_property_add_child(get_chardevs_root(), chr_new->label, + OBJECT(chr_new), &error_abort); + object_unref(OBJECT(chr_new)); + + ret = g_new0(ChardevReturn, 1); + if (CHARDEV_IS_PTY(chr_new)) { + ret->pty = g_strdup(chr_new->filename + 4); + ret->has_pty = true; + } + + return ret; +} + void qmp_chardev_remove(const char *id, Error **errp) { Chardev *chr; diff --git a/configure b/configure index 902653ae03..dceeb72e5e 100755 --- a/configure +++ b/configure @@ -1583,7 +1583,7 @@ gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits" gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags" gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags" -gcc_flags="-Wno-initializer-overrides $gcc_flags" +gcc_flags="-Wno-initializer-overrides -Wexpansion-to-defined $gcc_flags" gcc_flags="-Wno-string-plus-int $gcc_flags" # Note that we do not add -Werror to gcc_flags here, because that would # enable it for all configure tests. If a configure test failed due diff --git a/exec.c b/exec.c index a083ff89ad..01ac21e3cd 100644 --- a/exec.c +++ b/exec.c @@ -27,6 +27,7 @@ #include "exec/target_page.h" #include "tcg.h" #include "hw/qdev-core.h" +#include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) #include "hw/boards.h" #include "hw/xen/xen.h" @@ -480,19 +481,21 @@ static MemoryRegionSection address_space_do_translate(AddressSpace *as, { IOMMUTLBEntry iotlb; MemoryRegionSection *section; - MemoryRegion *mr; + IOMMUMemoryRegion *iommu_mr; + IOMMUMemoryRegionClass *imrc; for (;;) { AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch); section = address_space_translate_internal(d, addr, &addr, plen, is_mmio); - mr = section->mr; - if (!mr->iommu_ops) { + iommu_mr = memory_region_get_iommu(section->mr); + if (!iommu_mr) { break; } + imrc = memory_region_get_iommu_class_nocheck(iommu_mr); - iotlb = mr->iommu_ops->translate(mr, addr, is_write ? - IOMMU_WO : IOMMU_RO); + iotlb = imrc->translate(iommu_mr, addr, is_write ? + IOMMU_WO : IOMMU_RO); addr = ((iotlb.translated_addr & ~iotlb.addr_mask) | (addr & iotlb.addr_mask)); *plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1); @@ -588,7 +591,7 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, section = address_space_translate_internal(d, addr, xlat, plen, false); - assert(!section->mr->iommu_ops); + assert(!memory_region_is_iommu(section->mr)); return section; } #endif @@ -735,6 +738,20 @@ void cpu_exec_unrealizefn(CPUState *cpu) } } +Property cpu_common_props[] = { +#ifndef CONFIG_USER_ONLY + /* Create a memory property for softmmu CPU object, + * so users can wire up its memory. (This can't go in qom/cpu.c + * because that file is compiled only once for both user-mode + * and system builds.) The default if no link is set up is to use + * the system address space. + */ + DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), +#endif + DEFINE_PROP_END_OF_LIST(), +}; + void cpu_exec_initfn(CPUState *cpu) { cpu->as = NULL; @@ -742,18 +759,6 @@ void cpu_exec_initfn(CPUState *cpu) #ifndef CONFIG_USER_ONLY cpu->thread_id = qemu_get_thread_id(); - - /* This is a softmmu CPU object, so create a property for it - * so users can wire up its memory. (This can't go in qom/cpu.c - * because that file is compiled only once for both user-mode - * and system builds.) The default if no link is set up is to use - * the system address space. - */ - object_property_add_link(OBJECT(cpu), "memory", TYPE_MEMORY_REGION, - (Object **)&cpu->memory, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); cpu->memory = system_memory; object_ref(OBJECT(cpu->memory)); #endif @@ -775,15 +780,28 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp) #endif } +#if defined(CONFIG_USER_ONLY) static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { - /* Flush the whole TB as this will not have race conditions - * even if we don't have proper locking yet. - * Ideally we would just invalidate the TBs for the - * specified PC. - */ - tb_flush(cpu); + mmap_lock(); + tb_lock(); + tb_invalidate_phys_page_range(pc, pc + 1, 0); + tb_unlock(); + mmap_unlock(); } +#else +static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) +{ + MemTxAttrs attrs; + hwaddr phys = cpu_get_phys_page_attrs_debug(cpu, pc, &attrs); + int asidx = cpu_asidx_from_attrs(cpu, attrs); + if (phys != -1) { + /* Locks grabbed by tb_invalidate_phys_addr */ + tb_invalidate_phys_addr(cpu->cpu_ases[asidx].as, + phys | (pc & ~TARGET_PAGE_MASK)); + } +} +#endif #if defined(CONFIG_USER_ONLY) void cpu_watchpoint_remove_all(CPUState *cpu, int mask) @@ -2929,7 +2947,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr, } } else { /* RAM case */ - ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l); memcpy(ptr, buf, l); invalidate_and_set_dirty(mr, addr1, l); } @@ -3020,7 +3038,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr, } } else { /* RAM case */ - ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l); memcpy(buf, ptr, l); } diff --git a/gdbstub.c b/gdbstub.c index ec4e4b25be..f936ddd32d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -56,6 +56,21 @@ static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } +/* Return the GDB index for a given vCPU state. + * + * For user mode this is simply the thread id. In system mode GDB + * numbers CPUs from 1 as 0 is reserved as an "any cpu" index. + */ +static inline int cpu_gdb_index(CPUState *cpu) +{ +#if defined(CONFIG_USER_ONLY) + TaskState *ts = (TaskState *) cpu->opaque; + return ts->ts_tid; +#else + return cpu->cpu_index + 1; +#endif +} + enum { GDB_SIGNAL_0 = 0, GDB_SIGNAL_INT = 2, @@ -272,7 +287,20 @@ static int gdb_signal_to_target (int sig) return -1; } -//#define DEBUG_GDB +/* #define DEBUG_GDB */ + +#ifdef DEBUG_GDB +# define DEBUG_GDB_GATE 1 +#else +# define DEBUG_GDB_GATE 0 +#endif + +#define gdb_debug(fmt, ...) do { \ + if (DEBUG_GDB_GATE) { \ + fprintf(stderr, "%s: " fmt, __func__, ## __VA_ARGS__); \ + } \ +} while (0) + typedef struct GDBRegisterState { int base_reg; @@ -548,9 +576,7 @@ static int put_packet_binary(GDBState *s, const char *buf, int len) /* return -1 if error, 0 if OK */ static int put_packet(GDBState *s, const char *buf) { -#ifdef DEBUG_GDB - printf("reply='%s'\n", buf); -#endif + gdb_debug("reply='%s'\n", buf); return put_packet_binary(s, buf, strlen(buf)); } @@ -827,7 +853,7 @@ static CPUState *find_cpu(uint32_t thread_id) CPUState *cpu; CPU_FOREACH(cpu) { - if (cpu_index(cpu) == thread_id) { + if (cpu_gdb_index(cpu) == thread_id) { return cpu; } } @@ -912,23 +938,16 @@ static int gdb_handle_vcont(GDBState *s, const char *p) if (res) { goto out; } - idx = tmp; - /* 0 means any thread, so we pick the first valid CPU */ - if (!idx) { - idx = cpu_index(first_cpu); - } - /* - * If we are in user mode, the thread specified is actually a - * thread id, and not an index. We need to find the actual - * CPU first, and only then we can use its index. - */ - cpu = find_cpu(idx); + /* 0 means any thread, so we pick the first valid CPU */ + cpu = tmp ? find_cpu(tmp) : first_cpu; + /* invalid CPU/thread specified */ - if (!idx || !cpu) { + if (!cpu) { res = -EINVAL; goto out; } + /* only use if no previous match occourred */ if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; @@ -956,16 +975,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) uint8_t *registers; target_ulong addr, len; -#ifdef DEBUG_GDB - printf("command='%s'\n", line_buf); -#endif + + gdb_debug("command='%s'\n", line_buf); + p = line_buf; ch = *p++; switch(ch) { case '?': /* TODO: Make this return the correct value for user-mode. */ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, - cpu_index(s->c_cpu)); + cpu_gdb_index(s->c_cpu)); put_packet(s, buf); /* Remove all the breakpoints when this query is issued, * because gdb is doing and initial connect and the state @@ -1233,7 +1252,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } else if (strcmp(p,"sThreadInfo") == 0) { report_cpuinfo: if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu)); + snprintf(buf, sizeof(buf), "m%x", cpu_gdb_index(s->query_cpu)); put_packet(s, buf); s->query_cpu = CPU_NEXT(s->query_cpu); } else @@ -1390,7 +1409,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } snprintf(buf, sizeof(buf), "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, cpu_index(cpu), type, + GDB_SIGNAL_TRAP, cpu_gdb_index(cpu), type, (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; @@ -1424,7 +1443,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(cpu)); + snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_gdb_index(cpu)); send_packet: put_packet(s, buf); @@ -1519,17 +1538,14 @@ static void gdb_read_byte(GDBState *s, int ch) /* Waiting for a response to the last packet. If we see the start of a new command then abandon the previous response. */ if (ch == '-') { -#ifdef DEBUG_GDB - printf("Got NACK, retransmitting\n"); -#endif + gdb_debug("Got NACK, retransmitting\n"); put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + } else if (ch == '+') { + gdb_debug("Got ACK\n"); + } else { + gdb_debug("Got '%c' when expecting ACK/NACK\n", ch); } -#ifdef DEBUG_GDB - else if (ch == '+') - printf("Got ACK\n"); - else - printf("Got '%c' when expecting ACK/NACK\n", ch); -#endif + if (ch == '+' || ch == '$') s->last_packet_len = 0; if (ch != '$') @@ -1550,9 +1566,7 @@ static void gdb_read_byte(GDBState *s, int ch) s->line_sum = 0; s->state = RS_GETLINE; } else { -#ifdef DEBUG_GDB - printf("gdbstub received garbage between packets: 0x%x\n", ch); -#endif + gdb_debug("received garbage between packets: 0x%x\n", ch); } break; case RS_GETLINE: @@ -1568,9 +1582,7 @@ static void gdb_read_byte(GDBState *s, int ch) /* end of command, start of checksum*/ s->state = RS_CHKSUM1; } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { -#ifdef DEBUG_GDB - printf("gdbstub command buffer overrun, dropping command\n"); -#endif + gdb_debug("command buffer overrun, dropping command\n"); s->state = RS_IDLE; } else { /* unescaped command character */ @@ -1584,9 +1596,7 @@ static void gdb_read_byte(GDBState *s, int ch) s->state = RS_CHKSUM1; } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { /* command buffer overrun */ -#ifdef DEBUG_GDB - printf("gdbstub command buffer overrun, dropping command\n"); -#endif + gdb_debug("command buffer overrun, dropping command\n"); s->state = RS_IDLE; } else { /* parse escaped character and leave escape state */ @@ -1598,25 +1608,18 @@ static void gdb_read_byte(GDBState *s, int ch) case RS_GETLINE_RLE: if (ch < ' ') { /* invalid RLE count encoding */ -#ifdef DEBUG_GDB - printf("gdbstub got invalid RLE count: 0x%x\n", ch); -#endif + gdb_debug("got invalid RLE count: 0x%x\n", ch); s->state = RS_GETLINE; } else { /* decode repeat length */ int repeat = (unsigned char)ch - ' ' + 3; if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) { /* that many repeats would overrun the command buffer */ -#ifdef DEBUG_GDB - printf("gdbstub command buffer overrun," - " dropping command\n"); -#endif + gdb_debug("command buffer overrun, dropping command\n"); s->state = RS_IDLE; } else if (s->line_buf_index < 1) { /* got a repeat but we have nothing to repeat */ -#ifdef DEBUG_GDB - printf("gdbstub got invalid RLE sequence\n"); -#endif + gdb_debug("got invalid RLE sequence\n"); s->state = RS_GETLINE; } else { /* repeat the last character */ @@ -1631,9 +1634,7 @@ static void gdb_read_byte(GDBState *s, int ch) case RS_CHKSUM1: /* get high hex digit of checksum */ if (!isxdigit(ch)) { -#ifdef DEBUG_GDB - printf("gdbstub got invalid command checksum digit\n"); -#endif + gdb_debug("got invalid command checksum digit\n"); s->state = RS_GETLINE; break; } @@ -1644,21 +1645,17 @@ static void gdb_read_byte(GDBState *s, int ch) case RS_CHKSUM2: /* get low hex digit of checksum */ if (!isxdigit(ch)) { -#ifdef DEBUG_GDB - printf("gdbstub got invalid command checksum digit\n"); -#endif + gdb_debug("got invalid command checksum digit\n"); s->state = RS_GETLINE; break; } s->line_csum |= fromhex(ch); if (s->line_csum != (s->line_sum & 0xff)) { + gdb_debug("got command packet with incorrect checksum\n"); /* send NAK reply */ reply = '-'; put_buffer(s, &reply, 1); -#ifdef DEBUG_GDB - printf("gdbstub got command packet with incorrect checksum\n"); -#endif s->state = RS_IDLE; } else { /* send ACK reply */ @@ -2003,7 +2000,7 @@ int gdbserver_start(const char *device) if (chr) { qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL, NULL, true); + gdb_chr_event, NULL, NULL, NULL, true); } s->state = chr ? RS_IDLE : RS_INACTIVE; s->mon_chr = mon_chr; diff --git a/hmp-commands.hx b/hmp-commands.hx index 75f8bac01b..b3a8707dad 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1726,7 +1726,23 @@ ETEXI STEXI @item chardev-add args @findex chardev-add -chardev_add accepts the same parameters as the -chardev command line switch. +chardev-add accepts the same parameters as the -chardev command line switch. + +ETEXI + + { + .name = "chardev-change", + .args_type = "id:s,args:s", + .params = "id args", + .help = "change chardev", + .cmd = hmp_chardev_change, + }, + +STEXI +@item chardev-change args +@findex chardev-change +chardev-change accepts existing chardev @var{id} and then the same arguments +as the -chardev command line switch (except for "id"). ETEXI diff --git a/hmp.c b/hmp.c index 6d32c40723..d970ea9855 100644 --- a/hmp.c +++ b/hmp.c @@ -2197,6 +2197,40 @@ void hmp_chardev_add(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +void hmp_chardev_change(Monitor *mon, const QDict *qdict) +{ + const char *args = qdict_get_str(qdict, "args"); + const char *id; + Error *err = NULL; + ChardevBackend *backend = NULL; + ChardevReturn *ret = NULL; + QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), args, + true); + if (!opts) { + error_setg(&err, "Parsing chardev args failed"); + goto end; + } + + id = qdict_get_str(qdict, "id"); + if (qemu_opts_id(opts)) { + error_setg(&err, "Unexpected 'id' parameter"); + goto end; + } + + backend = qemu_chr_parse_opts(opts, &err); + if (!backend) { + goto end; + } + + ret = qmp_chardev_change(id, backend, &err); + +end: + qapi_free_ChardevReturn(ret); + qapi_free_ChardevBackend(backend); + qemu_opts_del(opts); + hmp_handle_error(mon, &err); +} + void hmp_chardev_remove(Monitor *mon, const QDict *qdict) { Error *local_err = NULL; diff --git a/hmp.h b/hmp.h index 214b2617e7..1ff455295e 100644 --- a/hmp.h +++ b/hmp.h @@ -102,6 +102,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); void hmp_chardev_add(Monitor *mon, const QDict *qdict); +void hmp_chardev_change(Monitor *mon, const QDict *qdict); void hmp_chardev_remove(Monitor *mon, const QDict *qdict); void hmp_chardev_send_break(Monitor *mon, const QDict *qdict); void hmp_qemu_io(Monitor *mon, const QDict *qdict); diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index c1cf7802a4..ae11e012c7 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -17,6 +17,7 @@ #define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" +#define TYPE_TYPHOON_IOMMU_MEMORY_REGION "typhoon-iommu-memory-region" typedef struct TyphoonCchip { MemoryRegion region; @@ -41,7 +42,7 @@ typedef struct TyphoonPchip { MemoryRegion reg_conf; AddressSpace iommu_as; - MemoryRegion iommu; + IOMMUMemoryRegion iommu; uint64_t ctl; TyphoonWindow win[4]; @@ -663,7 +664,8 @@ static bool window_translate(TyphoonWindow *win, hwaddr addr, /* Handle PCI-to-system address translation. */ /* TODO: A translation failure here ought to set PCI error codes on the Pchip and generate a machine check interrupt. */ -static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, IOMMUAccessFlags flag) { TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu); @@ -724,10 +726,6 @@ static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } -static const MemoryRegionIOMMUOps typhoon_iommu_ops = { - .translate = typhoon_translate_iommu, -}; - static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) { TyphoonState *s = opaque; @@ -891,9 +889,11 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, qdev_init_nofail(dev); /* Host memory as seen from the PCI side, via the IOMMU. */ - memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops, + memory_region_init_iommu(&s->pchip.iommu, sizeof(s->pchip.iommu), + TYPE_TYPHOON_IOMMU_MEMORY_REGION, OBJECT(s), "iommu-typhoon", UINT64_MAX); - address_space_init(&s->pchip.iommu_as, &s->pchip.iommu, "pchip0-pci"); + address_space_init(&s->pchip.iommu_as, MEMORY_REGION(&s->pchip.iommu), + "pchip0-pci"); pci_setup_iommu(b, typhoon_pci_dma_iommu, s); /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ @@ -951,9 +951,24 @@ static const TypeInfo typhoon_pcihost_info = { .class_init = typhoon_pcihost_class_init, }; +static void typhoon_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = typhoon_translate_iommu; +} + +static const TypeInfo typhoon_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_TYPHOON_IOMMU_MEMORY_REGION, + .class_init = typhoon_iommu_memory_region_class_init, +}; + static void typhoon_register_types(void) { type_register_static(&typhoon_pcihost_info); + type_register_static(&typhoon_iommu_memory_region_info); } type_init(typhoon_register_types) diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 629e6c64e6..731ed08de7 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1970,7 +1970,8 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error **errp) PXA2xxFIrState *s = PXA2XX_FIR(dev); qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty, - pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL, true); + pxa2xx_fir_rx, pxa2xx_fir_event, NULL, s, NULL, + true); } static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id) diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 7683edc9e5..6a45dcc009 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1106,7 +1106,7 @@ static void strongarm_uart_tx(void *opaque) if (s->utcr3 & UTCR3_LBM) /* loopback */ { strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1); - } else if (qemu_chr_fe_get_driver(&s->chr)) { + } else if (qemu_chr_fe_backend_connected(&s->chr)) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &s->tx_fifo[s->tx_start], 1); @@ -1247,7 +1247,7 @@ static void strongarm_uart_realize(DeviceState *dev, Error **errp) strongarm_uart_can_receive, strongarm_uart_receive, strongarm_uart_event, - s, NULL, true); + NULL, s, NULL, true); } static void strongarm_uart_reset(DeviceState *dev) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index c0bd247b37..b750bd8b53 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -983,10 +983,6 @@ static void virtio_blk_instance_init(Object *obj) { VirtIOBlock *s = VIRTIO_BLK(obj); - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&s->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); device_add_bootindex_property(obj, &s->conf.conf.bootindex, "bootindex", "/disk@0,0", DEVICE(obj), NULL); @@ -1014,6 +1010,8 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), + DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, + IOThread *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 4d46ad60ae..370dc7e296 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -279,7 +279,7 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp) BCM2835AuxState *s = BCM2835_AUX(dev); qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive, - bcm2835_aux_receive, NULL, s, NULL, true); + bcm2835_aux_receive, NULL, NULL, s, NULL, true); } static Property bcm2835_aux_props[] = { diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index 4a2c124104..6143494060 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -279,7 +279,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond, int ret; /* instant drain the fifo when there's no back-end */ - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { s->tx_count = 0; return FALSE; } @@ -485,7 +485,7 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp) fifo_trigger_update, s); qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void cadence_uart_init(Object *obj) diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index 762e3d8ada..95ccec6f8b 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -87,12 +87,12 @@ static const MemoryRegionOps debugcon_ops = { static void debugcon_realize_core(DebugconState *s, Error **errp) { - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create debugcon device, empty char device"); return; } - qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, s, NULL, true); + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, s, NULL, true); } static void debugcon_isa_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 34306e11ff..6ebcb87a40 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -146,7 +146,7 @@ static void digic_uart_realize(DeviceState *dev, Error **errp) DigicUartState *s = DIGIC_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void digic_uart_init(Object *obj) diff --git a/hw/char/escc.c b/hw/char/escc.c index 3f787632c7..89ae9eb997 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -417,7 +417,7 @@ static void escc_update_parameters(ChannelState *s) int speed, parity, data_bits, stop_bits; QEMUSerialSetParams ssp; - if (!qemu_chr_fe_get_driver(&s->chr) || s->type != ser) + if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != ser) return; if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) { @@ -557,7 +557,7 @@ static void escc_mem_write(void *opaque, hwaddr addr, trace_escc_mem_writeb_data(CHN_C(s), val); s->tx = val; if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &s->tx, 1); @@ -1013,10 +1013,10 @@ static void escc_realize(DeviceState *dev, Error **errp) ESCC_SIZE << s->it_shift); for (i = 0; i < 2; i++) { - if (qemu_chr_fe_get_driver(&s->chn[i].chr)) { + if (qemu_chr_fe_backend_connected(&s->chn[i].chr)) { s->chn[i].clock = s->frequency / 2; qemu_chr_fe_set_handlers(&s->chn[i].chr, serial_can_receive, - serial_receive1, serial_event, + serial_receive1, serial_event, NULL, &s->chn[i], NULL, true); } } diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c index c1fba9f50f..a184026410 100644 --- a/hw/char/etraxfs_ser.c +++ b/hw/char/etraxfs_ser.c @@ -233,7 +233,7 @@ static void etraxfs_ser_realize(DeviceState *dev, Error **errp) qemu_chr_fe_set_handlers(&s->chr, serial_can_receive, serial_receive, - serial_event, s, NULL, true); + serial_event, NULL, s, NULL, true); } static void etraxfs_ser_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index b51d44a321..3957e78abf 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -380,7 +380,7 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, break; case UTXH: - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | UTRSTAT_Tx_BUFFER_EMPTY); ch = (uint8_t)val; @@ -645,7 +645,7 @@ static void exynos4210_uart_realize(DeviceState *dev, Error **errp) qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive, exynos4210_uart_receive, exynos4210_uart_event, - s, NULL, true); + NULL, s, NULL, true); } static Property exynos4210_uart_properties[] = { diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index 32d98edf49..bac11bec58 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -201,7 +201,7 @@ static void grlib_apbuart_write(void *opaque, hwaddr addr, case DATA_OFFSET: case DATA_OFFSET + 3: /* When only one byte write */ /* Transmit when character device available and transmitter enabled */ - if (qemu_chr_fe_get_driver(&uart->chr) && + if (qemu_chr_fe_backend_connected(&uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) { c = value & 0xFF; /* XXX this blocks entire thread. Rewrite to use @@ -247,7 +247,7 @@ static int grlib_apbuart_init(SysBusDevice *dev) grlib_apbuart_can_receive, grlib_apbuart_receive, grlib_apbuart_event, - uart, NULL, true); + NULL, uart, NULL, true); sysbus_init_irq(dev, &uart->irq); diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index af250305be..70405ccf8b 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -315,7 +315,7 @@ static void imx_serial_realize(DeviceState *dev, Error **errp) DPRINTF("char dev for uart: %p\n", qemu_chr_fe_get_driver(&s->chr)); qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive, - imx_event, s, NULL, true); + imx_event, NULL, s, NULL, true); } static void imx_serial_init(Object *obj) diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index 337a3e566a..5e09caf851 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -542,10 +542,10 @@ static void ipoctal_realize(DeviceState *dev, Error **errp) ch->ipoctal = s; /* Redirect IP-Octal channels to host character devices */ - if (qemu_chr_fe_get_driver(&ch->dev)) { + if (qemu_chr_fe_backend_connected(&ch->dev)) { qemu_chr_fe_set_handlers(&ch->dev, hostdev_can_receive, hostdev_receive, hostdev_event, - ch, NULL, true); + NULL, ch, NULL, true); DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label); } else { DPRINTF("Could not redirect channel %u, no chardev set\n", i); diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c index 3948dcd332..d75c835ad2 100644 --- a/hw/char/lm32_juart.c +++ b/hw/char/lm32_juart.c @@ -119,7 +119,7 @@ static void lm32_juart_realize(DeviceState *dev, Error **errp) LM32JuartState *s = LM32_JUART(dev); qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx, - juart_event, s, NULL, true); + juart_event, NULL, s, NULL, true); } static const VMStateDescription vmstate_lm32_juart = { diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c index cff8c38f90..c4a3b9b275 100644 --- a/hw/char/lm32_uart.c +++ b/hw/char/lm32_uart.c @@ -266,7 +266,7 @@ static void lm32_uart_realize(DeviceState *dev, Error **errp) LM32UartState *s = LM32_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static const VMStateDescription vmstate_lm32_uart = { diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index fe12ad5ccb..56fa402b58 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -305,7 +305,7 @@ static void mcf_uart_realize(DeviceState *dev, Error **errp) mcf_uart_state *s = MCF_UART(dev); qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive, mcf_uart_receive, - mcf_uart_event, s, NULL, true); + mcf_uart_event, NULL, s, NULL, true); } static Property mcf_uart_properties[] = { diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index e19d0f6520..548ee27bca 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -199,7 +199,7 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp) MilkymistUartState *s = MILKYMIST_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void milkymist_uart_init(Object *obj) diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 75a1a2f55e..f79dc76543 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -503,6 +503,10 @@ static const VMStateDescription vmstate_parallel_isa = { } }; +static int parallel_can_receive(void *opaque) +{ + return 1; +} static void parallel_isa_realizefn(DeviceState *dev, Error **errp) { @@ -513,7 +517,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) int base; uint8_t dummy; - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create parallel device, empty char device"); return; } @@ -535,6 +539,8 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) isa_init_irq(isadev, &s->irq, isa->isairq); qemu_register_reset(parallel_reset, s); + qemu_chr_fe_set_handlers(&s->chr, parallel_can_receive, NULL, + NULL, NULL, s, NULL, true); if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) { s->hw_driver = 1; s->status = dummy; diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 33802f00c8..2aa277fc4f 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -329,7 +329,7 @@ static void pl011_realize(DeviceState *dev, Error **errp) PL011State *s = PL011(dev); qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive, - pl011_event, s, NULL, true); + pl011_event, NULL, s, NULL, true); } static void pl011_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 1b15046690..c500bdaf29 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -195,7 +195,7 @@ static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) { SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); - if (!qemu_chr_fe_get_driver(&scon->chr)) { + if (!qemu_chr_fe_backend_connected(&scon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } @@ -313,7 +313,7 @@ static int console_init(SCLPEvent *event) console_available = true; qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, - chr_read, NULL, scon, NULL, true); + chr_read, NULL, NULL, scon, NULL, true); return 0; } diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index 4a107a268d..d0265dfa7a 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -163,7 +163,7 @@ static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, { SCLPConsole *scon = SCLP_CONSOLE(event); - if (!qemu_chr_fe_get_driver(&scon->chr)) { + if (!qemu_chr_fe_backend_connected(&scon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } @@ -228,7 +228,7 @@ static int console_init(SCLPEvent *event) } console_available = true; qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, - chr_read, NULL, scon, NULL, true); + chr_read, NULL, NULL, scon, NULL, true); return 0; } diff --git a/hw/char/serial.c b/hw/char/serial.c index e1f12507bf..9aec6c60d8 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -312,6 +312,24 @@ static void serial_write_fcr(SerialState *s, uint8_t val) } } +static void serial_update_tiocm(SerialState *s) +{ + int flags; + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); + + flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); + + if (s->mcr & UART_MCR_RTS) { + flags |= CHR_TIOCM_RTS; + } + if (s->mcr & UART_MCR_DTR) { + flags |= CHR_TIOCM_DTR; + } + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); +} + static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -426,24 +444,13 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, break; case 4: { - int flags; int old_mcr = s->mcr; s->mcr = val & 0x1f; if (val & UART_MCR_LOOP) break; if (s->poll_msl >= 0 && old_mcr != s->mcr) { - - qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); - - flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); - - if (val & UART_MCR_RTS) - flags |= CHR_TIOCM_RTS; - if (val & UART_MCR_DTR) - flags |= CHR_TIOCM_DTR; - - qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); + serial_update_tiocm(s); /* Update the modem status after a one-character-send wait-time, since there may be a response from the device/computer at the other end of the serial line */ timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time); @@ -884,9 +891,37 @@ static void serial_reset(void *opaque) s->msr &= ~UART_MSR_ANY_DELTA; } +static int serial_be_change(void *opaque) +{ + SerialState *s = opaque; + + qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1, + serial_event, serial_be_change, s, NULL, true); + + serial_update_parameters(s); + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &s->last_break_enable); + + s->poll_msl = (s->ier & UART_IER_MSI) ? 1 : 0; + serial_update_msl(s); + + if (s->poll_msl >= 0 && !(s->mcr & UART_MCR_LOOP)) { + serial_update_tiocm(s); + } + + if (s->watch_tag > 0) { + g_source_remove(s->watch_tag); + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + serial_watch_cb, s); + } + + return 0; +} + void serial_realize_core(SerialState *s, Error **errp) { - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create serial device, empty char device"); return; } @@ -897,7 +932,7 @@ void serial_realize_core(SerialState *s, Error **errp) qemu_register_reset(serial_reset, s); qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1, - serial_event, s, NULL, true); + serial_event, serial_be_change, s, NULL, true); fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH); fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH); serial_reset(s); diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index ca9816d045..835b5378a0 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -110,7 +110,7 @@ static void sh_serial_write(void *opaque, hwaddr offs, } return; case 0x0c: /* FTDR / TDR */ - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { ch = val; /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ @@ -400,7 +400,7 @@ void sh_serial_init(MemoryRegion *sysmem, qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1, sh_serial_receive1, - sh_serial_event, s, NULL, true); + sh_serial_event, NULL, s, NULL, true); } s->eri = eri_source; diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 8f02f3a612..0fa416ca6b 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -78,13 +78,13 @@ static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) { VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); - if (!qemu_chr_fe_get_driver(&dev->chardev)) { + if (!qemu_chr_fe_backend_connected(&dev->chardev)) { error_setg(errp, "chardev property not set"); return; } qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive, - vty_receive, NULL, dev, NULL, true); + vty_receive, NULL, NULL, dev, NULL, true); } /* Forward declaration */ diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 59872e6d3b..268e435338 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -207,7 +207,8 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp) STM32F2XXUsartState *s = STM32F2XX_USART(dev); qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive, - stm32f2xx_usart_receive, NULL, s, NULL, true); + stm32f2xx_usart_receive, NULL, NULL, + s, NULL, true); } static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index 7b10a04f18..28f599111d 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -179,7 +179,7 @@ static void terminal_init(EmulatedCcw3270Device *dev, Error **errp) } terminal_available = true; qemu_chr_fe_set_handlers(&t->chr, terminal_can_read, - terminal_read, chr_event, t, NULL, true); + terminal_read, chr_event, NULL, t, NULL, true); } static int read_payload_3270(EmulatedCcw3270Device *dev, uint32_t cda, @@ -239,7 +239,7 @@ static int write_payload_3270(EmulatedCcw3270Device *dev, uint8_t cmd, return 0; } } - if (!qemu_chr_fe_get_driver(&t->chr)) { + if (!qemu_chr_fe_backend_connected(&t->chr)) { /* We just say we consumed all data if there's no backend. */ return count; } diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index 0cb1668c8a..198b2a89c0 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -49,7 +49,7 @@ static ssize_t flush_buf(VirtIOSerialPort *port, VirtConsole *vcon = VIRTIO_CONSOLE(port); ssize_t ret; - if (!qemu_chr_fe_get_driver(&vcon->chr)) { + if (!qemu_chr_fe_backend_connected(&vcon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } @@ -163,12 +163,35 @@ static void chr_event(void *opaque, int event) } } +static int chr_be_change(void *opaque) +{ + VirtConsole *vcon = opaque; + VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); + VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); + + if (k->is_console) { + qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, + NULL, chr_be_change, vcon, NULL, true); + } else { + qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, + chr_event, chr_be_change, vcon, NULL, false); + } + + if (vcon->watch) { + g_source_remove(vcon->watch); + vcon->watch = qemu_chr_fe_add_watch(&vcon->chr, + G_IO_OUT | G_IO_HUP, + chr_write_unblocked, vcon); + } + + return 0; +} + static void virtconsole_realize(DeviceState *dev, Error **errp) { VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); VirtConsole *vcon = VIRTIO_CONSOLE(dev); VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev); - Chardev *chr = qemu_chr_fe_get_driver(&vcon->chr); if (port->id == 0 && !k->is_console) { error_setg(errp, "Port number 0 on virtio-serial devices reserved " @@ -176,7 +199,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) return; } - if (chr) { + if (qemu_chr_fe_backend_connected(&vcon->chr)) { /* * For consoles we don't block guest data transfer just * because nothing is connected - we'll just let it go @@ -188,11 +211,13 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) */ if (k->is_console) { qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, - NULL, vcon, NULL, true); + NULL, chr_be_change, + vcon, NULL, true); virtio_serial_open(port); } else { qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, - chr_event, vcon, NULL, false); + chr_event, chr_be_change, + vcon, NULL, false); } } } diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index f9af8cadf4..3643dfe067 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -150,7 +150,7 @@ static void xencons_send(struct XenConsole *con) ssize_t len, size; size = con->buffer.size - con->buffer.consumed; - if (qemu_chr_fe_get_driver(&con->chr)) { + if (qemu_chr_fe_backend_connected(&con->chr)) { len = qemu_chr_fe_write(&con->chr, con->buffer.data + con->buffer.consumed, size); @@ -246,7 +246,7 @@ static int con_initialise(struct XenDevice *xendev) xen_be_bind_evtchn(&con->xendev); qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, - xencons_receive, NULL, con, NULL, true); + xencons_receive, NULL, NULL, con, NULL, true); xen_pv_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 71ed2fc1be..2a8bc1e497 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -212,7 +212,7 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) XilinxUARTLite *s = XILINX_UARTLITE(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void xilinx_uartlite_init(Object *obj) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 3bef41914d..ec10da7424 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -159,7 +159,7 @@ static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, set_pointer(obj, v, opaque, parse_drive, name, errp); } -PropertyInfo qdev_prop_drive = { +const PropertyInfo qdev_prop_drive = { .name = "str", .description = "Node name or ID of a block device to use as a backend", .get = get_drive, @@ -228,7 +228,7 @@ static void release_chr(Object *obj, const char *name, void *opaque) qemu_chr_fe_deinit(be, false); } -PropertyInfo qdev_prop_chr = { +const PropertyInfo qdev_prop_chr = { .name = "str", .description = "ID of a chardev to use as a backend", .get = get_chr, @@ -313,7 +313,7 @@ out: g_free(str); } -PropertyInfo qdev_prop_netdev = { +const PropertyInfo qdev_prop_netdev = { .name = "str", .description = "ID of a netdev to use as a backend", .get = get_netdev, @@ -393,7 +393,7 @@ static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque, *ptr = hubport; } -PropertyInfo qdev_prop_vlan = { +const PropertyInfo qdev_prop_vlan = { .name = "int32", .description = "Integer VLAN id to connect to", .print = print_vlan, diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 3d0bba21a2..dcecdf03e5 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -25,7 +25,8 @@ void qdev_prop_set_after_realize(DeviceState *dev, const char *name, } } -void qdev_prop_allow_set_link_before_realize(Object *obj, const char *name, +void qdev_prop_allow_set_link_before_realize(const Object *obj, + const char *name, Object *val, Error **errp) { DeviceState *dev = DEVICE(obj); @@ -131,7 +132,7 @@ static void set_default_value_bool(Object *obj, const Property *prop) object_property_set_bool(obj, prop->defval.u, prop->name, &error_abort); } -PropertyInfo qdev_prop_bit = { +const PropertyInfo qdev_prop_bit = { .name = "bool", .description = "on/off", .get = prop_get_bit, @@ -190,7 +191,7 @@ static void prop_set_bit64(Object *obj, Visitor *v, const char *name, bit64_prop_set(dev, prop, value); } -PropertyInfo qdev_prop_bit64 = { +const PropertyInfo qdev_prop_bit64 = { .name = "bool", .description = "on/off", .get = prop_get_bit64, @@ -225,7 +226,7 @@ static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_bool(v, name, ptr, errp); } -PropertyInfo qdev_prop_bool = { +const PropertyInfo qdev_prop_bool = { .name = "bool", .get = get_bool, .set = set_bool, @@ -269,7 +270,7 @@ static void set_default_value_uint(Object *obj, const Property *prop) object_property_set_uint(obj, prop->defval.u, prop->name, &error_abort); } -PropertyInfo qdev_prop_uint8 = { +const PropertyInfo qdev_prop_uint8 = { .name = "uint8", .get = get_uint8, .set = set_uint8, @@ -303,7 +304,7 @@ static void set_uint16(Object *obj, Visitor *v, const char *name, visit_type_uint16(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint16 = { +const PropertyInfo qdev_prop_uint16 = { .name = "uint16", .get = get_uint16, .set = set_uint16, @@ -362,14 +363,14 @@ static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_int32(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint32 = { +const PropertyInfo qdev_prop_uint32 = { .name = "uint32", .get = get_uint32, .set = set_uint32, .set_default_value = set_default_value_uint, }; -PropertyInfo qdev_prop_int32 = { +const PropertyInfo qdev_prop_int32 = { .name = "int32", .get = get_int32, .set = set_int32, @@ -403,7 +404,7 @@ static void set_uint64(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint64 = { +const PropertyInfo qdev_prop_uint64 = { .name = "uint64", .get = get_uint64, .set = set_uint64, @@ -456,7 +457,7 @@ static void set_string(Object *obj, Visitor *v, const char *name, *ptr = str; } -PropertyInfo qdev_prop_string = { +const PropertyInfo qdev_prop_string = { .name = "str", .release = release_string, .get = get_string, @@ -466,7 +467,7 @@ PropertyInfo qdev_prop_string = { /* --- pointer --- */ /* Not a proper property, just for dirty hacks. TODO Remove it! */ -PropertyInfo qdev_prop_ptr = { +const PropertyInfo qdev_prop_ptr = { .name = "ptr", }; @@ -540,7 +541,7 @@ inval: g_free(str); } -PropertyInfo qdev_prop_macaddr = { +const PropertyInfo qdev_prop_macaddr = { .name = "str", .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", .get = get_mac, @@ -549,7 +550,7 @@ PropertyInfo qdev_prop_macaddr = { /* --- on/off/auto --- */ -PropertyInfo qdev_prop_on_off_auto = { +const PropertyInfo qdev_prop_on_off_auto = { .name = "OnOffAuto", .description = "on/off/auto", .enum_table = OnOffAuto_lookup, @@ -562,7 +563,7 @@ PropertyInfo qdev_prop_on_off_auto = { QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); -PropertyInfo qdev_prop_losttickpolicy = { +const PropertyInfo qdev_prop_losttickpolicy = { .name = "LostTickPolicy", .enum_table = LostTickPolicy_lookup, .get = get_enum, @@ -574,7 +575,7 @@ PropertyInfo qdev_prop_losttickpolicy = { QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); -PropertyInfo qdev_prop_blockdev_on_error = { +const PropertyInfo qdev_prop_blockdev_on_error = { .name = "BlockdevOnError", .description = "Error handling policy, " "report/ignore/enospc/stop/auto", @@ -588,7 +589,7 @@ PropertyInfo qdev_prop_blockdev_on_error = { QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); -PropertyInfo qdev_prop_bios_chs_trans = { +const PropertyInfo qdev_prop_bios_chs_trans = { .name = "BiosAtaTranslation", .description = "Logical CHS translation algorithm, " "auto/none/lba/large/rechs", @@ -600,7 +601,7 @@ PropertyInfo qdev_prop_bios_chs_trans = { /* --- FDC default drive types */ -PropertyInfo qdev_prop_fdc_drive_type = { +const PropertyInfo qdev_prop_fdc_drive_type = { .name = "FdcDriveType", .description = "FDC drive type, " "144/288/120/none/auto", @@ -676,7 +677,7 @@ static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, } } -PropertyInfo qdev_prop_pci_devfn = { +const PropertyInfo qdev_prop_pci_devfn = { .name = "int32", .description = "Slot and optional function number, example: 06.0 or 06", .print = print_pci_devfn, @@ -725,7 +726,7 @@ static void set_blocksize(Object *obj, Visitor *v, const char *name, *ptr = value; } -PropertyInfo qdev_prop_blocksize = { +const PropertyInfo qdev_prop_blocksize = { .name = "uint16", .description = "A power of two between 512 and 32768", .get = get_uint16, @@ -840,7 +841,7 @@ inval: g_free(str); } -PropertyInfo qdev_prop_pci_host_devaddr = { +const PropertyInfo qdev_prop_pci_host_devaddr = { .name = "str", .description = "Address (bus/device/function) of " "the host device, example: 04:10.0", @@ -949,7 +950,7 @@ static void set_prop_arraylen(Object *obj, Visitor *v, const char *name, } } -PropertyInfo qdev_prop_arraylen = { +const PropertyInfo qdev_prop_arraylen = { .name = "uint32", .get = get_uint32, .set = set_prop_arraylen, @@ -1207,9 +1208,27 @@ static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_size(v, name, ptr, errp); } -PropertyInfo qdev_prop_size = { +const PropertyInfo qdev_prop_size = { .name = "size", .get = get_size, .set = set_size, .set_default_value = set_default_value_uint, }; + +/* --- object link property --- */ + +static void create_link_property(Object *obj, Property *prop, Error **errp) +{ + Object **child = qdev_get_prop_ptr(DEVICE(obj), prop); + + object_property_add_link(obj, prop->name, prop->link_type, + child, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + errp); +} + +const PropertyInfo qdev_prop_link = { + .name = "link", + .create = create_link_property, +}; diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 849952a8d4..ec63fe0354 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -744,6 +744,10 @@ static void qdev_property_add_legacy(DeviceState *dev, Property *prop, return; } + if (prop->info->create) { + return; + } + name = g_strdup_printf("legacy-%s", prop->name); object_property_add(OBJECT(dev), name, "str", prop->info->print ? qdev_get_legacy_property : prop->info->get, @@ -770,20 +774,23 @@ void qdev_property_add_static(DeviceState *dev, Property *prop, Error *local_err = NULL; Object *obj = OBJECT(dev); - /* - * TODO qdev_prop_ptr does not have getters or setters. It must - * go now that it can be replaced with links. The test should be - * removed along with it: all static properties are read/write. - */ - if (!prop->info->get && !prop->info->set) { - return; + if (prop->info->create) { + prop->info->create(obj, prop, &local_err); + } else { + /* + * TODO qdev_prop_ptr does not have getters or setters. It must + * go now that it can be replaced with links. The test should be + * removed along with it: all static properties are read/write. + */ + if (!prop->info->get && !prop->info->set) { + return; + } + object_property_add(obj, prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, + prop, &local_err); } - object_property_add(obj, prop->name, prop->info->name, - prop->info->get, prop->info->set, - prop->info->release, - prop, &local_err); - if (local_err) { error_propagate(errp, local_err); return; diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index a77d7db22f..561f828e7a 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -515,7 +515,7 @@ static void xlnx_dp_aux_set_command(XlnxDPState *s, uint32_t value) s->core_registers[DP_INTERRUPT_SIGNAL_STATE] |= 0x04; } -static void xlnx_dp_set_dpdma(Object *obj, const char *name, Object *val, +static void xlnx_dp_set_dpdma(const Object *obj, const char *name, Object *val, Error **errp) { XlnxDPState *s = XLNX_DP(obj); diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index edf9432051..5d4833eeca 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -54,6 +54,8 @@ typedef struct dma_pagetable_entry { #define RC4030(obj) \ OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030) +#define TYPE_RC4030_IOMMU_MEMORY_REGION "rc4030-iommu-memory-region" + typedef struct rc4030State { SysBusDevice parent; @@ -90,7 +92,7 @@ typedef struct rc4030State qemu_irq jazz_bus_irq; /* whole DMA memory region, root of DMA address space */ - MemoryRegion dma_mr; + IOMMUMemoryRegion dma_mr; AddressSpace dma_as; MemoryRegion iomem_chipset; @@ -488,7 +490,7 @@ static const MemoryRegionOps jazzio_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static IOMMUTLBEntry rc4030_dma_translate(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry rc4030_dma_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag) { rc4030State *s = container_of(iommu, rc4030State, dma_mr); @@ -516,10 +518,6 @@ static IOMMUTLBEntry rc4030_dma_translate(MemoryRegion *iommu, hwaddr addr, return ret; } -static const MemoryRegionIOMMUOps rc4030_dma_ops = { - .translate = rc4030_dma_translate, -}; - static void rc4030_reset(DeviceState *dev) { rc4030State *s = RC4030(dev); @@ -677,9 +675,10 @@ static void rc4030_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s, "rc4030.jazzio", 0x00001000); - memory_region_init_iommu(&s->dma_mr, o, &rc4030_dma_ops, - "rc4030.dma", UINT32_MAX); - address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma"); + memory_region_init_iommu(&s->dma_mr, sizeof(s->dma_mr), + TYPE_RC4030_IOMMU_MEMORY_REGION, + o, "rc4030.dma", UINT32_MAX); + address_space_init(&s->dma_as, MEMORY_REGION(&s->dma_mr), "rc4030-dma"); } static void rc4030_unrealize(DeviceState *dev, Error **errp) @@ -710,14 +709,29 @@ static const TypeInfo rc4030_info = { .class_init = rc4030_class_init, }; +static void rc4030_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = rc4030_dma_translate; +} + +static const TypeInfo rc4030_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_RC4030_IOMMU_MEMORY_REGION, + .class_init = rc4030_iommu_memory_region_class_init, +}; + static void rc4030_register_types(void) { type_register_static(&rc4030_info); + type_register_static(&rc4030_iommu_memory_region_info); } type_init(rc4030_register_types) -DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr) +DeviceState *rc4030_init(rc4030_dma **dmas, IOMMUMemoryRegion **dma_mr) { DeviceState *dev; diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index d93ffc2a15..334938a280 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -52,7 +52,7 @@ struct AMDVIAddressSpace { uint8_t bus_num; /* bus number */ uint8_t devfn; /* device function */ AMDVIState *iommu_state; /* AMDVI - one per machine */ - MemoryRegion iommu; /* Device's address translation region */ + IOMMUMemoryRegion iommu; /* Device's address translation region */ MemoryRegion iommu_ir; /* Device's interrupt remapping region */ AddressSpace as; /* device's corresponding address space */ }; @@ -987,7 +987,7 @@ static inline bool amdvi_is_interrupt_addr(hwaddr addr) return addr >= AMDVI_INT_ADDR_FIRST && addr <= AMDVI_INT_ADDR_LAST; } -static IOMMUTLBEntry amdvi_translate(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag) { AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu); @@ -1044,9 +1044,13 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) iommu_as[devfn]->devfn = (uint8_t)devfn; iommu_as[devfn]->iommu_state = s; - memory_region_init_iommu(&iommu_as[devfn]->iommu, OBJECT(s), - &s->iommu_ops, "amd-iommu", UINT64_MAX); - address_space_init(&iommu_as[devfn]->as, &iommu_as[devfn]->iommu, + memory_region_init_iommu(&iommu_as[devfn]->iommu, + sizeof(iommu_as[devfn]->iommu), + TYPE_AMD_IOMMU_MEMORY_REGION, + OBJECT(s), + "amd-iommu", UINT64_MAX); + address_space_init(&iommu_as[devfn]->as, + MEMORY_REGION(&iommu_as[devfn]->iommu), "amd-iommu"); } return &iommu_as[devfn]->as; @@ -1067,7 +1071,7 @@ static const MemoryRegionOps mmio_mem_ops = { } }; -static void amdvi_iommu_notify_flag_changed(MemoryRegion *iommu, +static void amdvi_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { @@ -1085,8 +1089,6 @@ static void amdvi_init(AMDVIState *s) { amdvi_iotlb_reset(s); - s->iommu_ops.translate = amdvi_translate; - s->iommu_ops.notify_flag_changed = amdvi_iommu_notify_flag_changed; s->devtab_len = 0; s->cmdbuf_len = 0; s->cmdbuf_head = 0; @@ -1227,10 +1229,25 @@ static const TypeInfo amdviPCI = { .instance_size = sizeof(AMDVIPCIState), }; +static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = amdvi_translate; + imrc->notify_flag_changed = amdvi_iommu_notify_flag_changed; +} + +static const TypeInfo amdvi_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_AMD_IOMMU_MEMORY_REGION, + .class_init = amdvi_iommu_memory_region_class_init, +}; + static void amdviPCI_register_types(void) { type_register_static(&amdviPCI); type_register_static(&amdvi); + type_register_static(&amdvi_iommu_memory_region_info); } type_init(amdviPCI_register_types); diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 0d3dc6a9f2..d370ae3549 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -220,6 +220,8 @@ #define TYPE_AMD_IOMMU_PCI "AMDVI-PCI" +#define TYPE_AMD_IOMMU_MEMORY_REGION "amd-iommu-iommu-memory-region" + typedef struct AMDVIAddressSpace AMDVIAddressSpace; /* functions to steal PCI config space */ @@ -276,9 +278,6 @@ typedef struct AMDVIState { uint8_t romask[AMDVI_MMIO_SIZE]; /* MMIO read/only mask */ bool mmio_enabled; - /* IOMMU function */ - MemoryRegionIOMMUOps iommu_ops; - /* for each served device */ AMDVIAddressSpace **address_spaces[PCI_BUS_MAX]; diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 88dc042b5c..e398746b4b 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -972,9 +972,9 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) /* Turn off first then on the other */ if (use_iommu) { memory_region_set_enabled(&as->sys_alias, false); - memory_region_set_enabled(&as->iommu, true); + memory_region_set_enabled(MEMORY_REGION(&as->iommu), true); } else { - memory_region_set_enabled(&as->iommu, false); + memory_region_set_enabled(MEMORY_REGION(&as->iommu), false); memory_region_set_enabled(&as->sys_alias, true); } @@ -1366,7 +1366,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, void *private) { - memory_region_notify_iommu((MemoryRegion *)private, *entry); + memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); return 0; } @@ -2264,7 +2264,7 @@ static void vtd_mem_write(void *opaque, hwaddr addr, } } -static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag) { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); @@ -2303,7 +2303,7 @@ static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr, return iotlb; } -static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu, +static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { @@ -2718,8 +2718,9 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) * vtd_sys_alias and intel_iommu regions. IR region is always * enabled. */ - memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s), - &s->iommu_ops, "intel_iommu_dmar", + memory_region_init_iommu(&vtd_dev_as->iommu, sizeof(vtd_dev_as->iommu), + TYPE_INTEL_IOMMU_MEMORY_REGION, OBJECT(s), + "intel_iommu_dmar", UINT64_MAX); memory_region_init_alias(&vtd_dev_as->sys_alias, OBJECT(s), "vtd_sys_alias", get_system_memory(), @@ -2736,7 +2737,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, &vtd_dev_as->sys_alias, 1); memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, - &vtd_dev_as->iommu, 1); + MEMORY_REGION(&vtd_dev_as->iommu), + 1); vtd_switch_address_space(vtd_dev_as); } return vtd_dev_as; @@ -2816,9 +2818,9 @@ static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) return 0; } -static void vtd_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) +static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) { - VTDAddressSpace *vtd_as = container_of(mr, VTDAddressSpace, iommu); + VTDAddressSpace *vtd_as = container_of(iommu_mr, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; uint8_t bus_n = pci_bus_num(vtd_as->bus); VTDContextEntry ce; @@ -2856,9 +2858,6 @@ static void vtd_init(IntelIOMMUState *s) memset(s->w1cmask, 0, DMAR_REG_SIZE); memset(s->womask, 0, DMAR_REG_SIZE); - s->iommu_ops.translate = vtd_iommu_translate; - s->iommu_ops.notify_flag_changed = vtd_iommu_notify_flag_changed; - s->iommu_ops.replay = vtd_iommu_replay; s->root = 0; s->root_extended = false; s->dmar_enabled = false; @@ -3073,9 +3072,26 @@ static const TypeInfo vtd_info = { .class_init = vtd_class_init, }; +static void vtd_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = vtd_iommu_translate; + imrc->notify_flag_changed = vtd_iommu_notify_flag_changed; + imrc->replay = vtd_iommu_replay; +} + +static const TypeInfo vtd_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_INTEL_IOMMU_MEMORY_REGION, + .class_init = vtd_iommu_memory_region_class_init, +}; + static void vtd_register_types(void) { type_register_static(&vtd_info); + type_register_static(&vtd_iommu_memory_region_info); } type_init(vtd_register_types) diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index 0d9ef77580..fc962c5fbc 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -383,8 +383,7 @@ static void patch_byte(X86CPU *cpu, target_ulong addr, uint8_t byte) cpu_memory_rw_debug(CPU(cpu), addr, &byte, 1, 1); } -static void patch_call(VAPICROMState *s, X86CPU *cpu, target_ulong ip, - uint32_t target) +static void patch_call(X86CPU *cpu, target_ulong ip, uint32_t target) { uint32_t offset; @@ -393,16 +392,59 @@ static void patch_call(VAPICROMState *s, X86CPU *cpu, target_ulong ip, cpu_memory_rw_debug(CPU(cpu), ip + 1, (void *)&offset, sizeof(offset), 1); } +typedef struct PatchInfo { + VAPICHandlers *handler; + target_ulong ip; +} PatchInfo; + +static void do_patch_instruction(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *x86_cpu = X86_CPU(cs); + PatchInfo *info = (PatchInfo *) data.host_ptr; + VAPICHandlers *handlers = info->handler; + target_ulong ip = info->ip; + uint8_t opcode[2]; + uint32_t imm32 = 0; + + cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0); + + switch (opcode[0]) { + case 0x89: /* mov r32 to r/m32 */ + patch_byte(x86_cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ + patch_call(x86_cpu, ip + 1, handlers->set_tpr); + break; + case 0x8b: /* mov r/m32 to r32 */ + patch_byte(x86_cpu, ip, 0x90); + patch_call(x86_cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); + break; + case 0xa1: /* mov abs to eax */ + patch_call(x86_cpu, ip, handlers->get_tpr[0]); + break; + case 0xa3: /* mov eax to abs */ + patch_call(x86_cpu, ip, handlers->set_tpr_eax); + break; + case 0xc7: /* mov imm32, r/m32 (c7/0) */ + patch_byte(x86_cpu, ip, 0x68); /* push imm32 */ + cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0); + cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1); + patch_call(x86_cpu, ip + 5, handlers->set_tpr); + break; + case 0xff: /* push r/m32 */ + patch_byte(x86_cpu, ip, 0x50); /* push eax */ + patch_call(x86_cpu, ip + 1, handlers->get_tpr_stack); + break; + default: + abort(); + } + + g_free(info); +} + static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) { CPUState *cs = CPU(cpu); - CPUX86State *env = &cpu->env; VAPICHandlers *handlers; - uint8_t opcode[2]; - uint32_t imm32 = 0; - target_ulong current_pc = 0; - target_ulong current_cs_base = 0; - uint32_t current_flags = 0; + PatchInfo *info; if (smp_cpus == 1) { handlers = &s->rom_state.up; @@ -410,60 +452,11 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) handlers = &s->rom_state.mp; } - if (tcg_enabled()) { - cpu_restore_state(cs, cs->mem_io_pc); - cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, - ¤t_flags); - /* Account this instruction, because we will exit the tb. - This is the first instruction in the block. Therefore - there is no need in restoring CPU state. */ - if (use_icount) { - --cs->icount_decr.u16.low; - } - } + info = g_new(PatchInfo, 1); + info->handler = handlers; + info->ip = ip; - pause_all_vcpus(); - - cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0); - - switch (opcode[0]) { - case 0x89: /* mov r32 to r/m32 */ - patch_byte(cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ - patch_call(s, cpu, ip + 1, handlers->set_tpr); - break; - case 0x8b: /* mov r/m32 to r32 */ - patch_byte(cpu, ip, 0x90); - patch_call(s, cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); - break; - case 0xa1: /* mov abs to eax */ - patch_call(s, cpu, ip, handlers->get_tpr[0]); - break; - case 0xa3: /* mov eax to abs */ - patch_call(s, cpu, ip, handlers->set_tpr_eax); - break; - case 0xc7: /* mov imm32, r/m32 (c7/0) */ - patch_byte(cpu, ip, 0x68); /* push imm32 */ - cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0); - cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1); - patch_call(s, cpu, ip + 5, handlers->set_tpr); - break; - case 0xff: /* push r/m32 */ - patch_byte(cpu, ip, 0x50); /* push eax */ - patch_call(s, cpu, ip + 1, handlers->get_tpr_stack); - break; - default: - abort(); - } - - resume_all_vcpus(); - - if (tcg_enabled()) { - /* Both tb_lock and iothread_mutex will be reset when - * longjmps back into the cpu_exec loop. */ - tb_lock(); - tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1); - cpu_loop_exit_noexc(cs); - } + async_safe_run_on_cpu(cs, do_patch_instruction, RUN_ON_CPU_HOST_PTR(info)); } void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip, diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index afafe1400f..b27babd504 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -90,7 +90,7 @@ static TypeInfo ipmi_interface_type_info = { .class_init = ipmi_interface_class_init, }; -static void isa_ipmi_bmc_check(Object *obj, const char *name, +static void isa_ipmi_bmc_check(const Object *obj, const char *name, Object *val, Error **errp) { IPMIBmc *bmc = IPMI_BMC(val); diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 329b03e17f..abab3bba4f 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -447,13 +447,13 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) { IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - if (!qemu_chr_fe_get_driver(&ibe->chr)) { + if (!qemu_chr_fe_backend_connected(&ibe->chr)) { error_setg(errp, "IPMI external bmc requires chardev attribute"); return; } qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, - chr_event, ibe, NULL, true); + chr_event, NULL, ibe, NULL, true); } static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index b72258e28f..ea67b461c2 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -350,6 +350,8 @@ static Property pc_dimm_properties[] = { DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot, PC_DIMM_UNASSIGNED_SLOT), + DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -367,33 +369,10 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, &value, errp); } -static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name, - Object *val, Error **errp) -{ - Error *local_err = NULL; - - if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { - char *path = object_get_canonical_path_component(val); - error_setg(&local_err, "can't use already busy memdev: %s", path); - g_free(path); - } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, &local_err); - } - - error_propagate(errp, local_err); -} - static void pc_dimm_init(Object *obj) { - PCDIMMDevice *dimm = PC_DIMM(obj); - object_property_add(obj, PC_DIMM_SIZE_PROP, "uint64", pc_dimm_get_size, NULL, NULL, NULL, &error_abort); - object_property_add_link(obj, PC_DIMM_MEMDEV_PROP, TYPE_MEMORY_BACKEND, - (Object **)&dimm->hostmem, - pc_dimm_check_memdev_is_busy, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); } static void pc_dimm_realize(DeviceState *dev, Error **errp) @@ -404,6 +383,11 @@ static void pc_dimm_realize(DeviceState *dev, Error **errp) if (!dimm->hostmem) { error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set"); return; + } else if (host_memory_backend_is_mapped(dimm->hostmem)) { + char *path = object_get_canonical_path_component(OBJECT(dimm->hostmem)); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + return; } if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) || (!nb_numa_nodes && dimm->node)) { diff --git a/hw/mips/boston.c b/hw/mips/boston.c index a4677f7da4..146be2ae74 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -533,7 +533,7 @@ static void boston_mach_init(MachineState *machine) chr = qemu_chr_new("lcd", "vc:320x240"); qemu_chr_fe_init(&s->lcd_display, chr, NULL); qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, - boston_lcd_event, s, NULL, true); + boston_lcd_event, NULL, s, NULL, true); ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, PCI_DEVFN(0, 0), diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 1cef581878..1f69322c15 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -130,7 +130,7 @@ static void mips_jazz_init(MachineState *machine, CPUMIPSState *env; qemu_irq *i8259; rc4030_dma *dmas; - MemoryRegion *rc4030_dma_mr; + IOMMUMemoryRegion *rc4030_dma_mr; MemoryRegion *isa_mem = g_new(MemoryRegion, 1); MemoryRegion *isa_io = g_new(MemoryRegion, 1); MemoryRegion *rtc = g_new(MemoryRegion, 1); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index dad2f37fb1..8cb9d3c3ce 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -570,7 +570,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, chr = qemu_chr_new("fpga", "vc:320x200"); qemu_chr_fe_init(&s->display, chr, NULL); qemu_chr_fe_set_handlers(&s->display, NULL, NULL, - malta_fgpa_display_event, s, NULL, true); + malta_fgpa_display_event, NULL, s, NULL, true); s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq, 230400, uart_chr, DEVICE_NATIVE_ENDIAN); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 2f0819d977..a58f9ee579 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -894,7 +894,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) } qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive, - ivshmem_read, NULL, s, NULL, true); + ivshmem_read, NULL, NULL, s, NULL, true); if (ivshmem_setup_interrupts(s, errp) < 0) { error_prepend(errp, "Failed to initialize interrupts: "); @@ -1009,18 +1009,6 @@ static const TypeInfo ivshmem_common_info = { .class_init = ivshmem_common_class_init, }; -static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, - Object *val, Error **errp) -{ - if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { - char *path = object_get_canonical_path_component(val); - error_setg(errp, "can't use already busy memdev: %s", path); - g_free(path); - } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, errp); - } -} - static const VMStateDescription ivshmem_plain_vmsd = { .name = TYPE_IVSHMEM_PLAIN, .version_id = 0, @@ -1037,6 +1025,8 @@ static const VMStateDescription ivshmem_plain_vmsd = { static Property ivshmem_plain_properties[] = { DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), + DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND, + HostMemoryBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -1044,11 +1034,6 @@ static void ivshmem_plain_init(Object *obj) { IVShmemState *s = IVSHMEM_PLAIN(obj); - object_property_add_link(obj, "memdev", TYPE_MEMORY_BACKEND, - (Object **)&s->hostmem, - ivshmem_check_memdev_is_busy, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); s->not_legacy_32bit = 1; } @@ -1059,6 +1044,11 @@ static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) if (!s->hostmem) { error_setg(errp, "You must specify a 'memdev'"); return; + } else if (host_memory_backend_is_mapped(s->hostmem)) { + char *path = object_get_canonical_path_component(OBJECT(s->hostmem)); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + return; } ivshmem_common_realize(dev, errp); @@ -1128,7 +1118,7 @@ static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM_COMMON(dev); - if (!qemu_chr_fe_get_driver(&s->server_chr)) { + if (!qemu_chr_fe_backend_connected(&s->server_chr)) { error_setg(errp, "You must specify a 'chardev'"); return; } @@ -1257,7 +1247,7 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp) " or ivshmem-doorbell instead"); } - if (!!qemu_chr_fe_get_driver(&s->server_chr) + !!s->shmobj != 1) { + if (qemu_chr_fe_backend_connected(&s->server_chr) + !!s->shmobj != 1) { error_setg(errp, "You must specify either 'shm' or 'chardev'"); return; } diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index a1edb53f95..211f6097fd 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -181,18 +181,6 @@ static void mips_gcr_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); MIPSGCRState *s = MIPS_GCR(obj); - object_property_add_link(obj, "gic", TYPE_MEMORY_REGION, - (Object **)&s->gic_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - - object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION, - (Object **)&s->cpc_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s, "mips-gcr", GCR_ADDRSPACE_SZ); sysbus_init_mmio(sbd, &s->iomem); @@ -227,6 +215,10 @@ static Property mips_gcr_properties[] = { DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1), DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800), DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR), + DEFINE_PROP_LINK("gic", MIPSGCRState, gic_mr, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_LINK("cpc", MIPSGCRState, cpc_mr, TYPE_MEMORY_REGION, + MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c index 326f5ef024..96e5d0b60d 100644 --- a/hw/pci-host/apb.c +++ b/hw/pci-host/apb.c @@ -123,7 +123,7 @@ do { printf("IOMMU: " fmt , ## __VA_ARGS__); } while (0) typedef struct IOMMUState { AddressSpace iommu_as; - MemoryRegion iommu; + IOMMUMemoryRegion iommu; uint64_t regs[IOMMU_NREGS]; } IOMMUState; @@ -133,6 +133,8 @@ typedef struct IOMMUState { #define APB_DEVICE(obj) \ OBJECT_CHECK(APBState, (obj), TYPE_APB) +#define TYPE_APB_IOMMU_MEMORY_REGION "pbm-iommu-memory-region" + typedef struct APBState { PCIHostState parent_obj; @@ -208,7 +210,7 @@ static AddressSpace *pbm_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) } /* Called from RCU critical section */ -static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry pbm_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag) { IOMMUState *is = container_of(iommu, IOMMUState, iommu); @@ -322,10 +324,6 @@ static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } -static MemoryRegionIOMMUOps pbm_iommu_ops = { - .translate = pbm_translate_iommu, -}; - static void iommu_config_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -697,9 +695,10 @@ PCIBus *pci_apb_init(hwaddr special_base, is = &d->iommu; memset(is, 0, sizeof(IOMMUState)); - memory_region_init_iommu(&is->iommu, OBJECT(dev), &pbm_iommu_ops, + memory_region_init_iommu(&is->iommu, sizeof(is->iommu), + TYPE_APB_IOMMU_MEMORY_REGION, OBJECT(dev), "iommu-apb", UINT64_MAX); - address_space_init(&is->iommu_as, &is->iommu, "pbm-as"); + address_space_init(&is->iommu_as, MEMORY_REGION(&is->iommu), "pbm-as"); pci_setup_iommu(phb->bus, pbm_pci_dma_iommu, is); /* APB secondary busses */ @@ -860,11 +859,25 @@ static const TypeInfo pbm_pci_bridge_info = { .class_init = pbm_pci_bridge_class_init, }; +static void pbm_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = pbm_translate_iommu; +} + +static const TypeInfo pbm_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_APB_IOMMU_MEMORY_REGION, + .class_init = pbm_iommu_memory_region_class_init, +}; + static void pbm_register_types(void) { type_register_static(&pbm_host_info); type_register_static(&pbm_pci_host_info); type_register_static(&pbm_pci_bridge_info); + type_register_static(&pbm_iommu_memory_region_info); } type_init(pbm_register_types) diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 583afc1a46..e614621a83 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -110,7 +110,8 @@ static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table) } /* Called from RCU critical section */ -static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, +static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, IOMMUAccessFlags flag) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); @@ -150,14 +151,14 @@ static void spapr_tce_table_pre_save(void *opaque) tcet->bus_offset, tcet->page_shift); } -static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu) +static uint64_t spapr_tce_get_min_page_size(IOMMUMemoryRegion *iommu) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); return 1ULL << tcet->page_shift; } -static void spapr_tce_notify_flag_changed(MemoryRegion *iommu, +static void spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { @@ -247,12 +248,6 @@ static const VMStateDescription vmstate_spapr_tce_table = { } }; -static MemoryRegionIOMMUOps spapr_iommu_ops = { - .translate = spapr_tce_translate_iommu, - .get_min_page_size = spapr_tce_get_min_page_size, - .notify_flag_changed = spapr_tce_notify_flag_changed, -}; - static int spapr_tce_table_realize(DeviceState *dev) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); @@ -265,7 +260,9 @@ static int spapr_tce_table_realize(DeviceState *dev) memory_region_init(&tcet->root, tcetobj, tmp, UINT64_MAX); snprintf(tmp, sizeof(tmp), "tce-iommu-%x", tcet->liobn); - memory_region_init_iommu(&tcet->iommu, tcetobj, &spapr_iommu_ops, tmp, 0); + memory_region_init_iommu(&tcet->iommu, sizeof(tcet->iommu), + TYPE_SPAPR_IOMMU_MEMORY_REGION, + tcetobj, tmp, 0); QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); @@ -348,9 +345,10 @@ void spapr_tce_table_enable(sPAPRTCETable *tcet, &tcet->fd, tcet->need_vfio); - memory_region_set_size(&tcet->iommu, + memory_region_set_size(MEMORY_REGION(&tcet->iommu), (uint64_t)tcet->nb_table << tcet->page_shift); - memory_region_add_subregion(&tcet->root, tcet->bus_offset, &tcet->iommu); + memory_region_add_subregion(&tcet->root, tcet->bus_offset, + MEMORY_REGION(&tcet->iommu)); } void spapr_tce_table_disable(sPAPRTCETable *tcet) @@ -359,8 +357,8 @@ void spapr_tce_table_disable(sPAPRTCETable *tcet) return; } - memory_region_del_subregion(&tcet->root, &tcet->iommu); - memory_region_set_size(&tcet->iommu, 0); + memory_region_del_subregion(&tcet->root, MEMORY_REGION(&tcet->iommu)); + memory_region_set_size(MEMORY_REGION(&tcet->iommu), 0); spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table); tcet->fd = -1; @@ -637,9 +635,25 @@ static TypeInfo spapr_tce_table_info = { .class_init = spapr_tce_table_class_init, }; +static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = spapr_tce_translate_iommu; + imrc->get_min_page_size = spapr_tce_get_min_page_size; + imrc->notify_flag_changed = spapr_tce_notify_flag_changed; +} + +static const TypeInfo spapr_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_SPAPR_IOMMU_MEMORY_REGION, + .class_init = spapr_iommu_memory_region_class_init, +}; + static void register_types(void) { type_register_static(&spapr_tce_table_info); + type_register_static(&spapr_iommu_memory_region_info); } type_init(register_types); diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index 80515eb54d..d2acd61a15 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -96,17 +96,11 @@ static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr, static void spapr_rng_instance_init(Object *obj) { - sPAPRRngState *rngstate = SPAPR_RNG(obj); - if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL) != NULL) { error_report("spapr-rng can not be instantiated twice!"); return; } - object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, - (Object **)&rngstate->backend, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); object_property_set_description(obj, "rng", "ID of the random number generator backend", NULL); @@ -163,6 +157,8 @@ int spapr_rng_populate_dt(void *fdt) static Property spapr_rng_properties[] = { DEFINE_PROP_BOOL("use-kvm", sPAPRRngState, use_kvm, false), + DEFINE_PROP_LINK("rng", sPAPRRngState, backend, TYPE_RNG_BACKEND, + RngBackend *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/css.c b/hw/s390x/css.c index d67fffae30..cd0b776861 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -2095,7 +2095,7 @@ out: g_free(str); } -PropertyInfo css_devid_propinfo = { +const PropertyInfo css_devid_propinfo = { .name = "str", .description = "Identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", @@ -2103,7 +2103,7 @@ PropertyInfo css_devid_propinfo = { .set = set_css_devid, }; -PropertyInfo css_devid_ro_propinfo = { +const PropertyInfo css_devid_ro_propinfo = { .name = "str", .description = "Read-only identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 5651483781..af702f8840 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -356,7 +356,7 @@ out: return pte; } -static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *mr, hwaddr addr, +static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, IOMMUAccessFlags flag) { uint64_t pte; @@ -407,10 +407,6 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *mr, hwaddr addr, return ret; } -static const MemoryRegionIOMMUOps s390_iommu_ops = { - .translate = s390_translate_iommu, -}; - static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus, int devfn) { @@ -522,17 +518,18 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { void s390_pci_iommu_enable(S390PCIIOMMU *iommu) { char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid); - memory_region_init_iommu(&iommu->iommu_mr, OBJECT(&iommu->mr), - &s390_iommu_ops, name, iommu->pal + 1); + memory_region_init_iommu(&iommu->iommu_mr, sizeof(iommu->iommu_mr), + TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr), + name, iommu->pal + 1); iommu->enabled = true; - memory_region_add_subregion(&iommu->mr, 0, &iommu->iommu_mr); + memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr)); g_free(name); } void s390_pci_iommu_disable(S390PCIIOMMU *iommu) { iommu->enabled = false; - memory_region_del_subregion(&iommu->mr, &iommu->iommu_mr); + memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr)); object_unparent(OBJECT(&iommu->iommu_mr)); } @@ -1018,7 +1015,7 @@ static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, zpci->fid_defined = true; } -static PropertyInfo s390_pci_fid_propinfo = { +static const PropertyInfo s390_pci_fid_propinfo = { .name = "zpci_fid", .get = s390_pci_get_fid, .set = s390_pci_set_fid, @@ -1058,12 +1055,26 @@ static TypeInfo s390_pci_iommu_info = { .instance_size = sizeof(S390PCIIOMMU), }; +static void s390_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = s390_translate_iommu; +} + +static const TypeInfo s390_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_S390_IOMMU_MEMORY_REGION, + .class_init = s390_iommu_memory_region_class_init, +}; + static void s390_pci_register_types(void) { type_register_static(&s390_pcihost_info); type_register_static(&s390_pcibus_info); type_register_static(&s390_pci_device_info); type_register_static(&s390_pci_iommu_info); + type_register_static(&s390_iommu_memory_region_info); } type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index cf142a3e68..67af2c12ff 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -24,6 +24,7 @@ #define TYPE_S390_PCI_BUS "s390-pcibus" #define TYPE_S390_PCI_DEVICE "zpci" #define TYPE_S390_PCI_IOMMU "s390-pci-iommu" +#define TYPE_S390_IOMMU_MEMORY_REGION "s390-iommu-memory-region" #define FH_MASK_ENABLE 0x80000000 #define FH_MASK_INSTANCE 0x7f000000 #define FH_MASK_SHM 0x00ff0000 @@ -266,7 +267,7 @@ typedef struct S390PCIIOMMU { S390PCIBusDevice *pbdev; AddressSpace as; MemoryRegion mr; - MemoryRegion iommu_mr; + IOMMUMemoryRegion iommu_mr; bool enabled; uint64_t g_iota; uint64_t pba; diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 8bc7c98682..b7beb8c36a 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -563,7 +563,8 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) S390PCIIOMMU *iommu; hwaddr start, end; IOMMUTLBEntry entry; - MemoryRegion *mr; + IOMMUMemoryRegion *iommu_mr; + IOMMUMemoryRegionClass *imrc; cpu_synchronize_state(CPU(cpu)); @@ -622,9 +623,11 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) goto out; } - mr = &iommu->iommu_mr; + iommu_mr = &iommu->iommu_mr; + imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + while (start < end) { - entry = mr->iommu_ops->translate(mr, start, IOMMU_NONE); + entry = imrc->translate(iommu_mr, start, IOMMU_NONE); if (!entry.translated_addr) { pbdev->state = ZPCI_FS_ERROR; @@ -635,7 +638,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) goto out; } - memory_region_notify_iommu(mr, entry); + memory_region_notify_iommu(iommu_mr, entry); start += entry.addr_mask + 1; } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index e18fd2600d..c07ddb1c94 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -867,8 +867,6 @@ static void virtio_ccw_blk_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", - &error_abort); object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), "bootindex", &error_abort); } @@ -952,8 +950,6 @@ static void virtio_ccw_scsi_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread", - &error_abort); } #ifdef CONFIG_VHOST_SCSI @@ -1552,8 +1548,6 @@ static void virtio_ccw_rng_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG); - object_property_add_alias(obj, "rng", OBJECT(&dev->vdev), - "rng", &error_abort); } static Property virtio_ccw_rng_properties[] = { @@ -1600,9 +1594,6 @@ static void virtio_ccw_crypto_instance_init(Object *obj) ccw_dev->force_revision_1 = true; virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_CRYPTO); - - object_property_add_alias(obj, "cryptodev", OBJECT(&dev->vdev), - "cryptodev", &error_abort); } static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index d076fe778b..eb639442d1 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -898,16 +898,6 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) virtio_scsi_dataplane_setup(s, errp); } -static void virtio_scsi_instance_init(Object *obj) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(obj); - - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&vs->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); -} - void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -935,6 +925,8 @@ static Property virtio_scsi_properties[] = { VIRTIO_SCSI_F_HOTPLUG, true), DEFINE_PROP_BIT("param_change", VirtIOSCSI, host_features, VIRTIO_SCSI_F_CHANGE, true), + DEFINE_PROP_LINK("iothread", VirtIOSCSI, parent_obj.conf.iothread, + TYPE_IOTHREAD, IOThread *), DEFINE_PROP_END_OF_LIST(), }; @@ -989,7 +981,6 @@ static const TypeInfo virtio_scsi_info = { .name = TYPE_VIRTIO_SCSI, .parent = TYPE_VIRTIO_SCSI_COMMON, .instance_size = sizeof(VirtIOSCSI), - .instance_init = virtio_scsi_instance_init, .class_init = virtio_scsi_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index ac1725eeae..45d96b03c6 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -322,7 +322,7 @@ static void passthru_apdu_from_guest( { PassthruState *card = PASSTHRU_CCID_CARD(base); - if (!qemu_chr_fe_get_driver(&card->cs)) { + if (!qemu_chr_fe_backend_connected(&card->cs)) { printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); return; } @@ -343,12 +343,12 @@ static int passthru_initfn(CCIDCardState *base) card->vscard_in_pos = 0; card->vscard_in_hdr = 0; - if (qemu_chr_fe_get_driver(&card->cs)) { + if (qemu_chr_fe_backend_connected(&card->cs)) { DPRINTF(card, D_INFO, "initing chardev\n"); qemu_chr_fe_set_handlers(&card->cs, ccid_card_vscard_can_read, ccid_card_vscard_read, - ccid_card_vscard_event, card, NULL, true); + ccid_card_vscard_event, NULL, card, NULL, true); ccid_card_vscard_send_init(card); } else { error_report("missing chardev"); diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index bfbf7cdce7..94b5c34afe 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -484,13 +484,12 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) { USBSerialState *s = USB_SERIAL_DEV(dev); Error *local_err = NULL; - Chardev *chr = qemu_chr_fe_get_driver(&s->cs); usb_desc_create_serial(dev); usb_desc_init(dev); dev->auto_attach = 0; - if (!chr) { + if (!qemu_chr_fe_backend_connected(&s->cs)) { error_setg(errp, "Property chardev is required"); return; } @@ -502,10 +501,10 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) } qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read, - usb_serial_event, s, NULL, true); + usb_serial_event, NULL, s, NULL, true); usb_serial_handle_reset(dev); - if (chr->be_open && !dev->attached) { + if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) { usb_device_attach(dev, &error_abort); } } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 5b65965cc2..5e42730449 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -273,10 +273,9 @@ static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond, static int usbredir_write(void *priv, uint8_t *data, int count) { USBRedirDevice *dev = priv; - Chardev *chr = qemu_chr_fe_get_driver(&dev->cs); int r; - if (!chr->be_open) { + if (!qemu_chr_fe_backend_open(&dev->cs)) { return 0; } @@ -1366,7 +1365,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp) USBRedirDevice *dev = USB_REDIRECT(udev); int i; - if (!qemu_chr_fe_get_driver(&dev->cs)) { + if (!qemu_chr_fe_backend_connected(&dev->cs)) { error_setg(errp, QERR_MISSING_PARAMETER, "chardev"); return; } @@ -1399,7 +1398,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp) /* Let the backend know we are ready */ qemu_chr_fe_set_handlers(&dev->cs, usbredir_chardev_can_read, usbredir_chardev_read, usbredir_chardev_event, - dev, NULL, true); + NULL, dev, NULL, true); dev->vmstate = qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 29923e4990..c1bb6d429a 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -479,6 +479,7 @@ static void vfio_listener_region_add(MemoryListener *listener, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); trace_vfio_listener_region_add_iommu(iova, end); /* @@ -488,7 +489,7 @@ static void vfio_listener_region_add(MemoryListener *listener, * device emulation the VFIO iommu handles to use). */ giommu = g_malloc0(sizeof(*giommu)); - giommu->iommu = section->mr; + giommu->iommu = iommu_mr; giommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; giommu->container = container; @@ -501,7 +502,7 @@ static void vfio_listener_region_add(MemoryListener *listener, int128_get64(llend)); QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); - memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); + memory_region_register_iommu_notifier(section->mr, &giommu->n); memory_region_iommu_replay(giommu->iommu, &giommu->n); return; @@ -569,9 +570,9 @@ static void vfio_listener_region_del(MemoryListener *listener, VFIOGuestIOMMU *giommu; QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { - if (giommu->iommu == section->mr && + if (MEMORY_REGION(giommu->iommu) == section->mr && giommu->n.start == section->offset_within_region) { - memory_region_unregister_iommu_notifier(giommu->iommu, + memory_region_unregister_iommu_notifier(section->mr, &giommu->n); QLIST_REMOVE(giommu, giommu_next); g_free(giommu); @@ -1163,7 +1164,8 @@ static void vfio_disconnect_container(VFIOGroup *group) QLIST_REMOVE(container, next); QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) { - memory_region_unregister_iommu_notifier(giommu->iommu, &giommu->n); + memory_region_unregister_iommu_notifier( + MEMORY_REGION(giommu->iommu), &giommu->n); QLIST_REMOVE(giommu, giommu_next); g_free(giommu); } diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 4409bcc0d7..32fd6a9b54 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -143,7 +143,8 @@ int vfio_spapr_create_window(VFIOContainer *container, hwaddr *pgsize) { int ret; - unsigned pagesize = memory_region_iommu_get_min_page_size(section->mr); + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + unsigned pagesize = memory_region_iommu_get_min_page_size(iommu_mr); unsigned entries, pages; struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) }; diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index 422aca3a98..bf64996e48 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -62,8 +62,6 @@ static void virtio_crypto_initfn(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_CRYPTO); - object_property_add_alias(obj, "cryptodev", OBJECT(&dev->vdev), - "cryptodev", &error_abort); } static const TypeInfo virtio_crypto_pci_info = { diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 0353eb6d5d..19c82e0432 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -781,6 +781,11 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp) if (vcrypto->cryptodev == NULL) { error_setg(errp, "'cryptodev' parameter expects a valid object"); return; + } else if (cryptodev_backend_is_used(vcrypto->cryptodev)) { + char *path = object_get_canonical_path_component(OBJECT(vcrypto->conf.cryptodev)); + error_setg(errp, "can't use already used cryptodev backend: %s", path); + g_free(path); + return; } vcrypto->max_queues = MAX(vcrypto->cryptodev->conf.peers.queues, 1); @@ -845,6 +850,8 @@ static const VMStateDescription vmstate_virtio_crypto = { }; static Property virtio_crypto_properties[] = { + DEFINE_PROP_LINK("cryptodev", VirtIOCrypto, conf.cryptodev, + TYPE_CRYPTODEV_BACKEND, CryptoDevBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -888,20 +895,6 @@ static void virtio_crypto_class_init(ObjectClass *klass, void *data) vdc->reset = virtio_crypto_reset; } -static void -virtio_crypto_check_cryptodev_is_used(Object *obj, const char *name, - Object *val, Error **errp) -{ - if (cryptodev_backend_is_used(CRYPTODEV_BACKEND(val))) { - char *path = object_get_canonical_path_component(val); - error_setg(errp, - "can't use already used cryptodev backend: %s", path); - g_free(path); - } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, errp); - } -} - static void virtio_crypto_instance_init(Object *obj) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(obj); @@ -911,12 +904,6 @@ static void virtio_crypto_instance_init(Object *obj) * Can be overriden with virtio_crypto_set_config_size. */ vcrypto->config_size = sizeof(struct virtio_crypto_config); - - object_property_add_link(obj, "cryptodev", - TYPE_CRYPTODEV_BACKEND, - (Object **)&vcrypto->conf.cryptodev, - virtio_crypto_check_cryptodev_is_used, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); } static const TypeInfo virtio_crypto_info = { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 93480a7af1..5d14bd66dc 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2000,8 +2000,6 @@ static void virtio_blk_pci_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", - &error_abort); object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), "bootindex", &error_abort); } @@ -2071,8 +2069,6 @@ static void virtio_scsi_pci_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread", - &error_abort); } static const TypeInfo virtio_scsi_pci_info = { @@ -2467,8 +2463,6 @@ static void virtio_rng_initfn(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG); - object_property_add_alias(obj, "rng", OBJECT(&dev->vdev), "rng", - &error_abort); } static const TypeInfo virtio_rng_pci_info = { diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index a6ee501051..289bbcac03 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -246,6 +246,7 @@ static Property virtio_rng_properties[] = { */ DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX), DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16), + DEFINE_PROP_LINK("rng", VirtIORNG, conf.rng, TYPE_RNG_BACKEND, RngBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -262,21 +263,10 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) vdc->get_features = get_features; } -static void virtio_rng_initfn(Object *obj) -{ - VirtIORNG *vrng = VIRTIO_RNG(obj); - - object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, - (Object **)&vrng->conf.rng, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); -} - static const TypeInfo virtio_rng_info = { .name = TYPE_VIRTIO_RNG, .parent = TYPE_VIRTIO_DEVICE, .instance_size = sizeof(VirtIORNG), - .instance_init = virtio_rng_initfn, .class_init = virtio_rng_class_init, }; diff --git a/include/block/nbd.h b/include/block/nbd.h index 6d75d5a670..9c3d0a5868 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Red Hat, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device @@ -83,18 +83,37 @@ typedef struct NBDReply NBDReply; #define NBD_FLAG_C_FIXED_NEWSTYLE (1 << 0) /* Fixed newstyle protocol. */ #define NBD_FLAG_C_NO_ZEROES (1 << 1) /* End handshake without zeroes. */ -/* Reply types. */ +/* Option requests. */ +#define NBD_OPT_EXPORT_NAME (1) +#define NBD_OPT_ABORT (2) +#define NBD_OPT_LIST (3) +/* #define NBD_OPT_PEEK_EXPORT (4) not in use */ +#define NBD_OPT_STARTTLS (5) +#define NBD_OPT_INFO (6) +#define NBD_OPT_GO (7) +#define NBD_OPT_STRUCTURED_REPLY (8) + +/* Option reply types. */ #define NBD_REP_ERR(value) ((UINT32_C(1) << 31) | (value)) #define NBD_REP_ACK (1) /* Data sending finished. */ #define NBD_REP_SERVER (2) /* Export description. */ +#define NBD_REP_INFO (3) /* NBD_OPT_INFO/GO. */ -#define NBD_REP_ERR_UNSUP NBD_REP_ERR(1) /* Unknown option */ -#define NBD_REP_ERR_POLICY NBD_REP_ERR(2) /* Server denied */ -#define NBD_REP_ERR_INVALID NBD_REP_ERR(3) /* Invalid length */ -#define NBD_REP_ERR_PLATFORM NBD_REP_ERR(4) /* Not compiled in */ -#define NBD_REP_ERR_TLS_REQD NBD_REP_ERR(5) /* TLS required */ -#define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */ +#define NBD_REP_ERR_UNSUP NBD_REP_ERR(1) /* Unknown option */ +#define NBD_REP_ERR_POLICY NBD_REP_ERR(2) /* Server denied */ +#define NBD_REP_ERR_INVALID NBD_REP_ERR(3) /* Invalid length */ +#define NBD_REP_ERR_PLATFORM NBD_REP_ERR(4) /* Not compiled in */ +#define NBD_REP_ERR_TLS_REQD NBD_REP_ERR(5) /* TLS required */ +#define NBD_REP_ERR_UNKNOWN NBD_REP_ERR(6) /* Export unknown */ +#define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */ +#define NBD_REP_ERR_BLOCK_SIZE_REQD NBD_REP_ERR(8) /* Need INFO_BLOCK_SIZE */ + +/* Info types, used during NBD_REP_INFO */ +#define NBD_INFO_EXPORT 0 +#define NBD_INFO_NAME 1 +#define NBD_INFO_DESCRIPTION 2 +#define NBD_INFO_BLOCK_SIZE 3 /* Request flags, sent from client to server during transmission phase */ #define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ @@ -123,13 +142,26 @@ enum { * aren't overflowing some other buffer. */ #define NBD_MAX_NAME_SIZE 256 +/* Details collected by NBD_OPT_EXPORT_NAME and NBD_OPT_GO */ +struct NBDExportInfo { + /* Set by client before nbd_receive_negotiate() */ + bool request_sizes; + /* Set by server results during nbd_receive_negotiate() */ + uint64_t size; + uint16_t flags; + uint32_t min_block; + uint32_t opt_block; + uint32_t max_block; +}; +typedef struct NBDExportInfo NBDExportInfo; + ssize_t nbd_rwv(QIOChannel *ioc, struct iovec *iov, size_t niov, size_t length, bool do_read, Error **errp); -int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, +int nbd_receive_negotiate(QIOChannel *ioc, const char *name, QCryptoTLSCreds *tlscreds, const char *hostname, - QIOChannel **outioc, - off_t *size, Error **errp); -int nbd_init(int fd, QIOChannelSocket *sioc, uint16_t flags, off_t size, + QIOChannel **outioc, NBDExportInfo *info, + Error **errp); +int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, Error **errp); ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request); ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp); diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 2cbb262f66..71cd069478 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -4,6 +4,7 @@ #include "chardev/char.h" typedef void IOEventHandler(void *opaque, int event); +typedef int BackendChangeHandler(void *opaque); /* This is the backend as seen by frontend, the actual backend is * Chardev */ @@ -12,6 +13,7 @@ struct CharBackend { IOEventHandler *chr_event; IOCanReadHandler *chr_can_read; IOReadHandler *chr_read; + BackendChangeHandler *chr_be_change; void *opaque; int tag; int fe_open; @@ -44,9 +46,26 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del); * * Returns the driver associated with a CharBackend or NULL if no * associated Chardev. + * Note: avoid this function as the driver should never be accessed directly, + * especially by the frontends that support chardevice hotswap. + * Consider qemu_chr_fe_backend_connected() to check for driver existence */ Chardev *qemu_chr_fe_get_driver(CharBackend *be); +/** + * @qemu_chr_fe_backend_connected: + * + * Returns true if there is a chardevice associated with @be. + */ +bool qemu_chr_fe_backend_connected(CharBackend *be); + +/** + * @qemu_chr_fe_backend_open: + * + * Returns true if chardevice associated with @be is open. + */ +bool qemu_chr_fe_backend_open(CharBackend *be); + /** * @qemu_chr_fe_set_handlers: * @b: a CharBackend @@ -54,6 +73,8 @@ Chardev *qemu_chr_fe_get_driver(CharBackend *be); * receive * @fd_read: callback to receive data from char * @fd_event: event callback + * @be_change: backend change callback; passing NULL means hot backend change + * is not supported and will not be attempted * @opaque: an opaque pointer for the callbacks * @context: a main loop context or NULL for the default * @set_open: whether to call qemu_chr_fe_set_open() implicitely when @@ -68,6 +89,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, IOEventHandler *fd_event, + BackendChangeHandler *be_change, void *opaque, GMainContext *context, bool set_open); diff --git a/include/chardev/char.h b/include/chardev/char.h index 8a9ade4931..1604ea9143 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -80,6 +80,16 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, */ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend); +/** + * @qemu_chr_parse_opts: + * + * Parse the options to the ChardevBackend struct. + * + * Returns: a new backend or NULL on error + */ +ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, + Error **errp); + /** * @qemu_chr_new: * @@ -92,6 +102,15 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend); */ Chardev *qemu_chr_new(const char *label, const char *filename); +/** + * @qemu_chr_change: + * + * Change an existing character backend + * + * @opts the new backend options + */ +void qemu_chr_change(QemuOpts *opts, Error **errp); + /** * @qemu_chr_cleanup: * diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 8096d64a1d..bf8da2aa5a 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -290,6 +290,9 @@ static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { } +static inline void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) +{ +} #endif #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index f9708bbcd6..9aa7756d92 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -58,15 +58,6 @@ void gdb_register_coprocessor(CPUState *cpu, gdb_reg_cb get_reg, gdb_reg_cb set_reg, int num_regs, const char *xml, int g_pos); -static inline int cpu_index(CPUState *cpu) -{ -#if defined(CONFIG_USER_ONLY) - return cpu->host_tid; -#else - return cpu->cpu_index + 1; -#endif -} - /* The GDB remote protocol transfers values in target byte order. This means * we can use the raw memory access routines to access the value buffer. * Conveniently, these also handle the case where the buffer is mis-aligned. diff --git a/include/exec/memory.h b/include/exec/memory.h index 8503685455..b7966014fe 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -25,6 +25,7 @@ #include "qemu/notify.h" #include "qom/object.h" #include "qemu/rcu.h" +#include "hw/qdev-core.h" #define RAM_ADDR_INVALID (~(ram_addr_t)0) @@ -35,6 +36,16 @@ #define MEMORY_REGION(obj) \ OBJECT_CHECK(MemoryRegion, (obj), TYPE_MEMORY_REGION) +#define TYPE_IOMMU_MEMORY_REGION "qemu:iommu-memory-region" +#define IOMMU_MEMORY_REGION(obj) \ + OBJECT_CHECK(IOMMUMemoryRegion, (obj), TYPE_IOMMU_MEMORY_REGION) +#define IOMMU_MEMORY_REGION_CLASS(klass) \ + OBJECT_CLASS_CHECK(IOMMUMemoryRegionClass, (klass), \ + TYPE_IOMMU_MEMORY_REGION) +#define IOMMU_MEMORY_REGION_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IOMMUMemoryRegionClass, (obj), \ + TYPE_IOMMU_MEMORY_REGION) + typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegionMmio MemoryRegionMmio; @@ -189,26 +200,27 @@ struct MemoryRegionOps { const MemoryRegionMmio old_mmio; }; -typedef struct MemoryRegionIOMMUOps MemoryRegionIOMMUOps; +typedef struct IOMMUMemoryRegionClass { + /* private */ + struct DeviceClass parent_class; -struct MemoryRegionIOMMUOps { /* * Return a TLB entry that contains a given address. Flag should * be the access permission of this translation operation. We can * set flag to IOMMU_NONE to mean that we don't need any * read/write permission checks, like, when for region replay. */ - IOMMUTLBEntry (*translate)(MemoryRegion *iommu, hwaddr addr, + IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag); /* Returns minimum supported page size */ - uint64_t (*get_min_page_size)(MemoryRegion *iommu); + uint64_t (*get_min_page_size)(IOMMUMemoryRegion *iommu); /* Called when IOMMU Notifier flag changed */ - void (*notify_flag_changed)(MemoryRegion *iommu, + void (*notify_flag_changed)(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old_flags, IOMMUNotifierFlag new_flags); /* Set this up to provide customized IOMMU replay function */ - void (*replay)(MemoryRegion *iommu, IOMMUNotifier *notifier); -}; + void (*replay)(IOMMUMemoryRegion *iommu, IOMMUNotifier *notifier); +} IOMMUMemoryRegionClass; typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; @@ -227,9 +239,9 @@ struct MemoryRegion { bool flush_coalesced_mmio; bool global_locking; uint8_t dirty_log_mask; + bool is_iommu; RAMBlock *ram_block; Object *owner; - const MemoryRegionIOMMUOps *iommu_ops; const MemoryRegionOps *ops; void *opaque; @@ -252,6 +264,11 @@ struct MemoryRegion { const char *name; unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; +}; + +struct IOMMUMemoryRegion { + MemoryRegion parent_obj; + QLIST_HEAD(, IOMMUNotifier) iommu_notify; IOMMUNotifierFlag iommu_notify_flags; }; @@ -612,21 +629,24 @@ static inline void memory_region_init_reservation(MemoryRegion *mr, } /** - * memory_region_init_iommu: Initialize a memory region that translates - * addresses + * memory_region_init_iommu: Initialize a memory region of a custom type + * that translates addresses * * An IOMMU region translates addresses and forwards accesses to a target * memory region. * - * @mr: the #MemoryRegion to be initialized + * @typename: QOM class name + * @_iommu_mr: the #IOMMUMemoryRegion to be initialized + * @instance_size: the IOMMUMemoryRegion subclass instance size * @owner: the object that tracks the region's reference count * @ops: a function that translates addresses into the @target region * @name: used for debugging; not visible to the user or ABI * @size: size of the region. */ -void memory_region_init_iommu(MemoryRegion *mr, - struct Object *owner, - const MemoryRegionIOMMUOps *ops, +void memory_region_init_iommu(void *_iommu_mr, + size_t instance_size, + const char *mrtypename, + Object *owner, const char *name, uint64_t size); @@ -679,20 +699,40 @@ static inline bool memory_region_is_romd(MemoryRegion *mr) } /** - * memory_region_is_iommu: check whether a memory region is an iommu + * memory_region_get_iommu: check whether a memory region is an iommu * - * Returns %true is a memory region is an iommu. + * Returns pointer to IOMMUMemoryRegion if a memory region is an iommu, + * otherwise NULL. * * @mr: the memory region being queried */ -static inline bool memory_region_is_iommu(MemoryRegion *mr) +static inline IOMMUMemoryRegion *memory_region_get_iommu(MemoryRegion *mr) { if (mr->alias) { - return memory_region_is_iommu(mr->alias); + return memory_region_get_iommu(mr->alias); } - return mr->iommu_ops; + if (mr->is_iommu) { + return (IOMMUMemoryRegion *) mr; + } + return NULL; } +/** + * memory_region_get_iommu_class_nocheck: returns iommu memory region class + * if an iommu or NULL if not + * + * Returns pointer to IOMMUMemoryRegioniClass if a memory region is an iommu, + * otherwise NULL. This is fast path avoinding QOM checking, use with caution. + * + * @mr: the memory region being queried + */ +static inline IOMMUMemoryRegionClass *memory_region_get_iommu_class_nocheck( + IOMMUMemoryRegion *iommu_mr) +{ + return (IOMMUMemoryRegionClass *) (((Object *)iommu_mr)->class); +} + +#define memory_region_is_iommu(mr) (memory_region_get_iommu(mr) != NULL) /** * memory_region_iommu_get_min_page_size: get minimum supported page size @@ -700,9 +740,9 @@ static inline bool memory_region_is_iommu(MemoryRegion *mr) * * Returns minimum supported page size for an iommu. * - * @mr: the memory region being queried + * @iommu_mr: the memory region being queried */ -uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr); +uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr); /** * memory_region_notify_iommu: notify a change in an IOMMU translation entry. @@ -716,12 +756,12 @@ uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr); * Note: for any IOMMU implementation, an in-place mapping change * should be notified with an UNMAP followed by a MAP. * - * @mr: the memory region that was changed + * @iommu_mr: the memory region that was changed * @entry: the new entry in the IOMMU translation table. The entry * replaces all old entries for the same virtual I/O address range. * Deleted entries have .@perm == 0. */ -void memory_region_notify_iommu(MemoryRegion *mr, +void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, IOMMUTLBEntry entry); /** @@ -756,18 +796,18 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, * a notifier with the minimum page granularity returned by * mr->iommu_ops->get_page_size(). * - * @mr: the memory region to observe + * @iommu_mr: the memory region to observe * @n: the notifier to which to replay iommu mappings */ -void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n); +void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n); /** * memory_region_iommu_replay_all: replay existing IOMMU translations * to all the notifiers registered. * - * @mr: the memory region to observe + * @iommu_mr: the memory region to observe */ -void memory_region_iommu_replay_all(MemoryRegion *mr); +void memory_region_iommu_replay_all(IOMMUMemoryRegion *iommu_mr); /** * memory_region_unregister_iommu_notifier: unregister a notifier for diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 3e51876b75..08d8a26d13 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -32,6 +32,8 @@ #define INTEL_IOMMU_DEVICE(obj) \ OBJECT_CHECK(IntelIOMMUState, (obj), TYPE_INTEL_IOMMU_DEVICE) +#define TYPE_INTEL_IOMMU_MEMORY_REGION "intel-iommu-iommu-memory-region" + /* DMAR Hardware Unit Definition address (IOMMU unit) */ #define Q35_HOST_BRIDGE_IOMMU_ADDR 0xfed90000ULL @@ -83,7 +85,7 @@ struct VTDAddressSpace { PCIBus *bus; uint8_t devfn; AddressSpace as; - MemoryRegion iommu; + IOMMUMemoryRegion iommu; MemoryRegion root; MemoryRegion sys_alias; MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ @@ -289,7 +291,6 @@ struct IntelIOMMUState { uint32_t context_cache_gen; /* Should be in [1,MAX] */ GHashTable *iotlb; /* IOTLB */ - MemoryRegionIOMMUOps iommu_ops; GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ /* list of registered notifiers */ diff --git a/include/hw/mips/mips.h b/include/hw/mips/mips.h index 16412dc150..2f6774d540 100644 --- a/include/hw/mips/mips.h +++ b/include/hw/mips/mips.h @@ -19,6 +19,6 @@ typedef struct rc4030DMAState *rc4030_dma; void rc4030_dma_read(void *dma, uint8_t *buf, int len); void rc4030_dma_write(void *dma, uint8_t *buf, int len); -DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr); +DeviceState *rc4030_init(rc4030_dma **dmas, IOMMUMemoryRegion **dma_mr); #endif diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index a184ffab0e..5f708eec23 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -582,6 +582,10 @@ typedef struct sPAPRTCETable sPAPRTCETable; #define SPAPR_TCE_TABLE(obj) \ OBJECT_CHECK(sPAPRTCETable, (obj), TYPE_SPAPR_TCE_TABLE) +#define TYPE_SPAPR_IOMMU_MEMORY_REGION "spapr-iommu-memory-region" +#define SPAPR_IOMMU_MEMORY_REGION(obj) \ + OBJECT_CHECK(IOMMUMemoryRegion, (obj), TYPE_SPAPR_IOMMU_MEMORY_REGION) + struct sPAPRTCETable { DeviceState parent; uint32_t liobn; @@ -594,7 +598,8 @@ struct sPAPRTCETable { bool bypass; bool need_vfio; int fd; - MemoryRegion root, iommu; + MemoryRegion root; + IOMMUMemoryRegion iommu; struct VIOsPAPRDevice *vdev; /* for @bypass migration compatibility only */ QLIST_ENTRY(sPAPRTCETable) list; }; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 9d7c1c0e9b..53488153fd 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -223,7 +223,7 @@ struct BusState { struct Property { const char *name; - PropertyInfo *info; + const PropertyInfo *info; ptrdiff_t offset; uint8_t bitnr; union { @@ -231,8 +231,9 @@ struct Property { uint64_t u; } defval; int arrayoffset; - PropertyInfo *arrayinfo; + const PropertyInfo *arrayinfo; int arrayfieldsize; + const char *link_type; }; struct PropertyInfo { @@ -241,6 +242,7 @@ struct PropertyInfo { const char * const *enum_table; int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len); void (*set_default_value)(Object *obj, const Property *prop); + void (*create)(Object *obj, Property *prop, Error **errp); ObjectPropertyAccessor *get; ObjectPropertyAccessor *set; ObjectPropertyRelease *release; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 0604c337e0..f6692d5dc3 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -5,31 +5,32 @@ /*** qdev-properties.c ***/ -extern PropertyInfo qdev_prop_bit; -extern PropertyInfo qdev_prop_bit64; -extern PropertyInfo qdev_prop_bool; -extern PropertyInfo qdev_prop_uint8; -extern PropertyInfo qdev_prop_uint16; -extern PropertyInfo qdev_prop_uint32; -extern PropertyInfo qdev_prop_int32; -extern PropertyInfo qdev_prop_uint64; -extern PropertyInfo qdev_prop_size; -extern PropertyInfo qdev_prop_string; -extern PropertyInfo qdev_prop_chr; -extern PropertyInfo qdev_prop_ptr; -extern PropertyInfo qdev_prop_macaddr; -extern PropertyInfo qdev_prop_on_off_auto; -extern PropertyInfo qdev_prop_losttickpolicy; -extern PropertyInfo qdev_prop_blockdev_on_error; -extern PropertyInfo qdev_prop_bios_chs_trans; -extern PropertyInfo qdev_prop_fdc_drive_type; -extern PropertyInfo qdev_prop_drive; -extern PropertyInfo qdev_prop_netdev; -extern PropertyInfo qdev_prop_vlan; -extern PropertyInfo qdev_prop_pci_devfn; -extern PropertyInfo qdev_prop_blocksize; -extern PropertyInfo qdev_prop_pci_host_devaddr; -extern PropertyInfo qdev_prop_arraylen; +extern const PropertyInfo qdev_prop_bit; +extern const PropertyInfo qdev_prop_bit64; +extern const PropertyInfo qdev_prop_bool; +extern const PropertyInfo qdev_prop_uint8; +extern const PropertyInfo qdev_prop_uint16; +extern const PropertyInfo qdev_prop_uint32; +extern const PropertyInfo qdev_prop_int32; +extern const PropertyInfo qdev_prop_uint64; +extern const PropertyInfo qdev_prop_size; +extern const PropertyInfo qdev_prop_string; +extern const PropertyInfo qdev_prop_chr; +extern const PropertyInfo qdev_prop_ptr; +extern const PropertyInfo qdev_prop_macaddr; +extern const PropertyInfo qdev_prop_on_off_auto; +extern const PropertyInfo qdev_prop_losttickpolicy; +extern const PropertyInfo qdev_prop_blockdev_on_error; +extern const PropertyInfo qdev_prop_bios_chs_trans; +extern const PropertyInfo qdev_prop_fdc_drive_type; +extern const PropertyInfo qdev_prop_drive; +extern const PropertyInfo qdev_prop_netdev; +extern const PropertyInfo qdev_prop_vlan; +extern const PropertyInfo qdev_prop_pci_devfn; +extern const PropertyInfo qdev_prop_blocksize; +extern const PropertyInfo qdev_prop_pci_host_devaddr; +extern const PropertyInfo qdev_prop_arraylen; +extern const PropertyInfo qdev_prop_link; #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \ .name = (_name), \ @@ -117,6 +118,14 @@ extern PropertyInfo qdev_prop_arraylen; .arrayoffset = offsetof(_state, _arrayfield), \ } +#define DEFINE_PROP_LINK(_name, _state, _field, _type, _ptr_type) { \ + .name = (_name), \ + .info = &(qdev_prop_link), \ + .offset = offsetof(_state, _field) \ + + type_check(_ptr_type, typeof_field(_state, _field)), \ + .link_type = _type, \ + } + #define DEFINE_PROP_UINT8(_n, _s, _f, _d) \ DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_uint8, uint8_t) #define DEFINE_PROP_UINT16(_n, _s, _f, _d) \ @@ -272,7 +281,8 @@ void qdev_prop_set_after_realize(DeviceState *dev, const char *name, * This function should be used as the check() argument to * object_property_add_link(). */ -void qdev_prop_allow_set_link_before_realize(Object *obj, const char *name, +void qdev_prop_allow_set_link_before_realize(const Object *obj, + const char *name, Object *val, Error **errp); #endif diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index eb0e26f258..dc1001bee1 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -112,7 +112,7 @@ typedef struct CssDevId { bool valid; } CssDevId; -extern PropertyInfo css_devid_propinfo; +extern const PropertyInfo css_devid_propinfo; #define DEFINE_PROP_CSS_DEV_ID(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, css_devid_propinfo, CssDevId) @@ -196,7 +196,7 @@ int css_do_rchp(uint8_t cssid, uint8_t chpid); bool css_present(uint8_t cssid); #endif -extern PropertyInfo css_devid_ro_propinfo; +extern const PropertyInfo css_devid_ro_propinfo; #define DEFINE_PROP_CSS_DEV_ID_RO(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, css_devid_ro_propinfo, CssDevId) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 0b475a3596..f3a2ac9fee 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -95,7 +95,7 @@ typedef struct VFIOContainer { typedef struct VFIOGuestIOMMU { VFIOContainer *container; - MemoryRegion *iommu; + IOMMUMemoryRegion *iommu; hwaddr iommu_offset; IOMMUNotifier n; QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 2706aabedf..b19159104c 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -45,6 +45,7 @@ typedef struct MachineState MachineState; typedef struct MemoryListener MemoryListener; typedef struct MemoryMappingList MemoryMappingList; typedef struct MemoryRegion MemoryRegion; +typedef struct IOMMUMemoryRegion IOMMUMemoryRegion; typedef struct MemoryRegionCache MemoryRegionCache; typedef struct MemoryRegionSection MemoryRegionSection; typedef struct MigrationIncomingState MigrationIncomingState; diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 7bfd50cc32..04c31e63eb 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -265,7 +265,6 @@ struct qemu_work_item; * @cpu_index: CPU index (informative). * @nr_cores: Number of cores within this CPU package. * @nr_threads: Number of threads within this CPU. - * @host_tid: Host thread ID. * @running: #true if CPU is currently running (lockless). * @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end; * valid under cpu_list_lock. @@ -319,7 +318,6 @@ struct CPUState { HANDLE hThread; #endif int thread_id; - uint32_t host_tid; bool running, has_waiter; struct QemuCond *halt_cond; bool thread_kicked; @@ -1015,6 +1013,7 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx); void QEMU_NORETURN cpu_abort(CPUState *cpu, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +extern Property cpu_common_props[]; void cpu_exec_initfn(CPUState *cpu); void cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); diff --git a/include/qom/object.h b/include/qom/object.h index abaeb8cf4e..1b828994fa 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -764,7 +764,7 @@ ObjectClass *object_get_class(Object *obj); * * Returns: The QOM typename of @obj. */ -const char *object_get_typename(Object *obj); +const char *object_get_typename(const Object *obj); /** * type_register_static: @@ -1319,7 +1319,7 @@ typedef enum { * callback function. It allows the link property to be set and never returns * an error. */ -void object_property_allow_set_link(Object *, const char *, +void object_property_allow_set_link(const Object *, const char *, Object *, Error **); /** @@ -1352,7 +1352,7 @@ void object_property_allow_set_link(Object *, const char *, */ void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, - void (*check)(Object *obj, const char *name, + void (*check)(const Object *obj, const char *name, Object *val, Error **errp), ObjectPropertyLinkFlags flags, Error **errp); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 925ae11ea6..003943b736 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6219,7 +6219,6 @@ static void *clone_func(void *arg) thread_cpu = cpu; ts = (TaskState *)cpu->opaque; info->tid = gettid(); - cpu->host_tid = info->tid; task_settid(ts); if (info->child_tidptr) put_user_u32(info->tid, info->child_tidptr); diff --git a/memory.c b/memory.c index 1044bbaf0c..69f697c20e 100644 --- a/memory.c +++ b/memory.c @@ -977,12 +977,11 @@ static char *memory_region_escape_name(const char *name) return escaped; } -void memory_region_init(MemoryRegion *mr, - Object *owner, - const char *name, - uint64_t size) +static void memory_region_do_init(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size) { - object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION); mr->size = int128_make64(size); if (size == UINT64_MAX) { mr->size = int128_2_64(); @@ -1006,6 +1005,15 @@ void memory_region_init(MemoryRegion *mr, } } +void memory_region_init(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size) +{ + object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION); + memory_region_do_init(mr, owner, name, size); +} + static void memory_region_get_addr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -1092,6 +1100,13 @@ static void memory_region_initfn(Object *obj) NULL, NULL, &error_abort); } +static void iommu_memory_region_initfn(Object *obj) +{ + MemoryRegion *mr = MEMORY_REGION(obj); + + mr->is_iommu = true; +} + static uint64_t unassigned_mem_read(void *opaque, hwaddr addr, unsigned size) { @@ -1491,17 +1506,23 @@ void memory_region_init_rom_device(MemoryRegion *mr, mr->ram_block = qemu_ram_alloc(size, mr, errp); } -void memory_region_init_iommu(MemoryRegion *mr, +void memory_region_init_iommu(void *_iommu_mr, + size_t instance_size, + const char *mrtypename, Object *owner, - const MemoryRegionIOMMUOps *ops, const char *name, uint64_t size) { - memory_region_init(mr, owner, name, size); - mr->iommu_ops = ops, + struct IOMMUMemoryRegion *iommu_mr; + struct MemoryRegion *mr; + + object_initialize(_iommu_mr, instance_size, mrtypename); + mr = MEMORY_REGION(_iommu_mr); + memory_region_do_init(mr, owner, name, size); + iommu_mr = IOMMU_MEMORY_REGION(mr); mr->terminates = true; /* then re-forwards */ - QLIST_INIT(&mr->iommu_notify); - mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE; + QLIST_INIT(&iommu_mr->iommu_notify); + iommu_mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE; } static void memory_region_finalize(Object *obj) @@ -1596,63 +1617,70 @@ bool memory_region_is_logging(MemoryRegion *mr, uint8_t client) return memory_region_get_dirty_log_mask(mr) & (1 << client); } -static void memory_region_update_iommu_notify_flags(MemoryRegion *mr) +static void memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr) { IOMMUNotifierFlag flags = IOMMU_NOTIFIER_NONE; IOMMUNotifier *iommu_notifier; + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); - IOMMU_NOTIFIER_FOREACH(iommu_notifier, mr) { + IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) { flags |= iommu_notifier->notifier_flags; } - if (flags != mr->iommu_notify_flags && - mr->iommu_ops->notify_flag_changed) { - mr->iommu_ops->notify_flag_changed(mr, mr->iommu_notify_flags, - flags); + if (flags != iommu_mr->iommu_notify_flags && imrc->notify_flag_changed) { + imrc->notify_flag_changed(iommu_mr, + iommu_mr->iommu_notify_flags, + flags); } - mr->iommu_notify_flags = flags; + iommu_mr->iommu_notify_flags = flags; } void memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n) { + IOMMUMemoryRegion *iommu_mr; + if (mr->alias) { memory_region_register_iommu_notifier(mr->alias, n); return; } /* We need to register for at least one bitfield */ + iommu_mr = IOMMU_MEMORY_REGION(mr); assert(n->notifier_flags != IOMMU_NOTIFIER_NONE); assert(n->start <= n->end); - QLIST_INSERT_HEAD(&mr->iommu_notify, n, node); - memory_region_update_iommu_notify_flags(mr); + QLIST_INSERT_HEAD(&iommu_mr->iommu_notify, n, node); + memory_region_update_iommu_notify_flags(iommu_mr); } -uint64_t memory_region_iommu_get_min_page_size(MemoryRegion *mr) +uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr) { - assert(memory_region_is_iommu(mr)); - if (mr->iommu_ops && mr->iommu_ops->get_min_page_size) { - return mr->iommu_ops->get_min_page_size(mr); + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + + if (imrc->get_min_page_size) { + return imrc->get_min_page_size(iommu_mr); } return TARGET_PAGE_SIZE; } -void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) +void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) { + MemoryRegion *mr = MEMORY_REGION(iommu_mr); + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); hwaddr addr, granularity; IOMMUTLBEntry iotlb; /* If the IOMMU has its own replay callback, override */ - if (mr->iommu_ops->replay) { - mr->iommu_ops->replay(mr, n); + if (imrc->replay) { + imrc->replay(iommu_mr, n); return; } - granularity = memory_region_iommu_get_min_page_size(mr); + granularity = memory_region_iommu_get_min_page_size(iommu_mr); for (addr = 0; addr < memory_region_size(mr); addr += granularity) { - iotlb = mr->iommu_ops->translate(mr, addr, IOMMU_NONE); + iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE); if (iotlb.perm != IOMMU_NONE) { n->notify(n, &iotlb); } @@ -1665,24 +1693,27 @@ void memory_region_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) } } -void memory_region_iommu_replay_all(MemoryRegion *mr) +void memory_region_iommu_replay_all(IOMMUMemoryRegion *iommu_mr) { IOMMUNotifier *notifier; - IOMMU_NOTIFIER_FOREACH(notifier, mr) { - memory_region_iommu_replay(mr, notifier); + IOMMU_NOTIFIER_FOREACH(notifier, iommu_mr) { + memory_region_iommu_replay(iommu_mr, notifier); } } void memory_region_unregister_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n) { + IOMMUMemoryRegion *iommu_mr; + if (mr->alias) { memory_region_unregister_iommu_notifier(mr->alias, n); return; } QLIST_REMOVE(n, node); - memory_region_update_iommu_notify_flags(mr); + iommu_mr = IOMMU_MEMORY_REGION(mr); + memory_region_update_iommu_notify_flags(iommu_mr); } void memory_region_notify_one(IOMMUNotifier *notifier, @@ -1710,14 +1741,14 @@ void memory_region_notify_one(IOMMUNotifier *notifier, } } -void memory_region_notify_iommu(MemoryRegion *mr, +void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, IOMMUTLBEntry entry) { IOMMUNotifier *iommu_notifier; - assert(memory_region_is_iommu(mr)); + assert(memory_region_is_iommu(MEMORY_REGION(iommu_mr))); - IOMMU_NOTIFIER_FOREACH(iommu_notifier, mr) { + IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) { memory_region_notify_one(iommu_notifier, &entry); } } @@ -2825,9 +2856,19 @@ static const TypeInfo memory_region_info = { .instance_finalize = memory_region_finalize, }; +static const TypeInfo iommu_memory_region_info = { + .parent = TYPE_MEMORY_REGION, + .name = TYPE_IOMMU_MEMORY_REGION, + .class_size = sizeof(IOMMUMemoryRegionClass), + .instance_size = sizeof(IOMMUMemoryRegion), + .instance_init = iommu_memory_region_initfn, + .abstract = true, +}; + static void memory_register_types(void) { type_register_static(&memory_region_info); + type_register_static(&iommu_memory_region_info); } type_init(memory_register_types) diff --git a/monitor.c b/monitor.c index 12935a7d71..534f4e3079 100644 --- a/monitor.c +++ b/monitor.c @@ -4110,12 +4110,12 @@ void monitor_init(Chardev *chr, int flags) if (monitor_is_qmp(mon)) { qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read, - monitor_qmp_event, mon, NULL, true); + monitor_qmp_event, NULL, mon, NULL, true); qemu_chr_fe_set_echo(&mon->chr, true); json_message_parser_init(&mon->qmp.parser, handle_qmp_command); } else { qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read, - monitor_event, mon, NULL, true); + monitor_event, NULL, mon, NULL, true); } qemu_mutex_lock(&monitor_lock); diff --git a/nbd/client.c b/nbd/client.c index 9c52b9b885..c3ee9f36b1 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Red Hat, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device Client Side @@ -104,7 +104,7 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, if (len == -1) { req.length = len = strlen(data); } - trace_nbd_send_option_request(opt, len); + trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); stq_be_p(&req.magic, NBD_OPTS_MAGIC); stl_be_p(&req.option, opt); @@ -154,7 +154,9 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, be32_to_cpus(&reply->type); be32_to_cpus(&reply->length); - trace_nbd_receive_option_reply(reply->option, reply->type, reply->length); + trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), + reply->type, nbd_rep_lookup(reply->type), + reply->length); if (reply->magic != NBD_REP_MAGIC) { error_setg(errp, "Unexpected option reply magic"); @@ -188,12 +190,16 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply, if (reply->length) { if (reply->length > NBD_MAX_BUFFER_SIZE) { - error_setg(errp, "server's error message is too long"); + error_setg(errp, "server error 0x%" PRIx32 + " (%s) message is too long", + reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg = g_malloc(reply->length + 1); if (nbd_read(ioc, msg, reply->length, errp) < 0) { - error_prepend(errp, "failed to read option error message"); + error_prepend(errp, "failed to read option error 0x%" PRIx32 + " (%s) message", + reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg[reply->length] = '\0'; @@ -201,38 +207,48 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply, switch (reply->type) { case NBD_REP_ERR_UNSUP: - trace_nbd_reply_err_unsup(reply->option); + trace_nbd_reply_err_unsup(reply->option, nbd_opt_lookup(reply->option)); result = 0; goto cleanup; case NBD_REP_ERR_POLICY: - error_setg(errp, "Denied by server for option %" PRIx32, - reply->option); + error_setg(errp, "Denied by server for option %" PRIx32 " (%s)", + reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_INVALID: - error_setg(errp, "Invalid data length for option %" PRIx32, - reply->option); + error_setg(errp, "Invalid data length for option %" PRIx32 " (%s)", + reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_PLATFORM: - error_setg(errp, "Server lacks support for option %" PRIx32, - reply->option); + error_setg(errp, "Server lacks support for option %" PRIx32 " (%s)", + reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_TLS_REQD: - error_setg(errp, "TLS negotiation required before option %" PRIx32, - reply->option); + error_setg(errp, "TLS negotiation required before option %" PRIx32 + " (%s)", reply->option, nbd_opt_lookup(reply->option)); + break; + + case NBD_REP_ERR_UNKNOWN: + error_setg(errp, "Requested export not available for option %" PRIx32 + " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_SHUTDOWN: - error_setg(errp, "Server shutting down before option %" PRIx32, - reply->option); + error_setg(errp, "Server shutting down before option %" PRIx32 " (%s)", + reply->option, nbd_opt_lookup(reply->option)); + break; + + case NBD_REP_ERR_BLOCK_SIZE_REQD: + error_setg(errp, "Server requires INFO_BLOCK_SIZE for option %" PRIx32 + " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; default: - error_setg(errp, "Unknown error code when asking for option %" PRIx32, - reply->option); + error_setg(errp, "Unknown error code when asking for option %" PRIx32 + " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; } @@ -334,6 +350,165 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, } +/* Returns -1 if NBD_OPT_GO proves the export @wantname cannot be + * used, 0 if NBD_OPT_GO is unsupported (fall back to NBD_OPT_LIST and + * NBD_OPT_EXPORT_NAME in that case), and > 0 if the export is good to + * go (with @info populated). */ +static int nbd_opt_go(QIOChannel *ioc, const char *wantname, + NBDExportInfo *info, Error **errp) +{ + nbd_opt_reply reply; + uint32_t len = strlen(wantname); + uint16_t type; + int error; + char *buf; + + /* The protocol requires that the server send NBD_INFO_EXPORT with + * a non-zero flags (at least NBD_FLAG_HAS_FLAGS must be set); so + * flags still 0 is a witness of a broken server. */ + info->flags = 0; + + trace_nbd_opt_go_start(wantname); + buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); + stl_be_p(buf, len); + memcpy(buf + 4, wantname, len); + /* At most one request, everything else up to server */ + stw_be_p(buf + 4 + len, info->request_sizes); + if (info->request_sizes) { + stw_be_p(buf + 4 + len + 2, NBD_INFO_BLOCK_SIZE); + } + if (nbd_send_option_request(ioc, NBD_OPT_GO, + 4 + len + 2 + 2 * info->request_sizes, buf, + errp) < 0) { + return -1; + } + + while (1) { + if (nbd_receive_option_reply(ioc, NBD_OPT_GO, &reply, errp) < 0) { + return -1; + } + error = nbd_handle_reply_err(ioc, &reply, errp); + if (error <= 0) { + return error; + } + len = reply.length; + + if (reply.type == NBD_REP_ACK) { + /* Server is done sending info and moved into transmission + phase, but make sure it sent flags */ + if (len) { + error_setg(errp, "server sent invalid NBD_REP_ACK"); + nbd_send_opt_abort(ioc); + return -1; + } + if (!info->flags) { + error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); + nbd_send_opt_abort(ioc); + return -1; + } + trace_nbd_opt_go_success(); + return 1; + } + if (reply.type != NBD_REP_INFO) { + error_setg(errp, "unexpected reply type %" PRIx32 + " (%s), expected %x", + reply.type, nbd_rep_lookup(reply.type), NBD_REP_INFO); + nbd_send_opt_abort(ioc); + return -1; + } + if (len < sizeof(type)) { + error_setg(errp, "NBD_REP_INFO length %" PRIu32 " is too short", + len); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &type, sizeof(type), errp) < 0) { + error_prepend(errp, "failed to read info type"); + nbd_send_opt_abort(ioc); + return -1; + } + len -= sizeof(type); + be16_to_cpus(&type); + switch (type) { + case NBD_INFO_EXPORT: + if (len != sizeof(info->size) + sizeof(info->flags)) { + error_setg(errp, "remaining export info len %" PRIu32 + " is unexpected size", len); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { + error_prepend(errp, "failed to read info size"); + nbd_send_opt_abort(ioc); + return -1; + } + be64_to_cpus(&info->size); + if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { + error_prepend(errp, "failed to read info flags"); + nbd_send_opt_abort(ioc); + return -1; + } + be16_to_cpus(&info->flags); + trace_nbd_receive_negotiate_size_flags(info->size, info->flags); + break; + + case NBD_INFO_BLOCK_SIZE: + if (len != sizeof(info->min_block) * 3) { + error_setg(errp, "remaining export info len %" PRIu32 + " is unexpected size", len); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &info->min_block, sizeof(info->min_block), + errp) < 0) { + error_prepend(errp, "failed to read info minimum block size"); + nbd_send_opt_abort(ioc); + return -1; + } + be32_to_cpus(&info->min_block); + if (!is_power_of_2(info->min_block)) { + error_setg(errp, "server minimum block size %" PRId32 + "is not a power of two", info->min_block); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &info->opt_block, sizeof(info->opt_block), + errp) < 0) { + error_prepend(errp, "failed to read info preferred block size"); + nbd_send_opt_abort(ioc); + return -1; + } + be32_to_cpus(&info->opt_block); + if (!is_power_of_2(info->opt_block) || + info->opt_block < info->min_block) { + error_setg(errp, "server preferred block size %" PRId32 + "is not valid", info->opt_block); + nbd_send_opt_abort(ioc); + return -1; + } + if (nbd_read(ioc, &info->max_block, sizeof(info->max_block), + errp) < 0) { + error_prepend(errp, "failed to read info maximum block size"); + nbd_send_opt_abort(ioc); + return -1; + } + be32_to_cpus(&info->max_block); + trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block, + info->max_block); + break; + + default: + trace_nbd_opt_go_info_unknown(type, nbd_info_lookup(type)); + if (nbd_drop(ioc, len, errp) < 0) { + error_prepend(errp, "Failed to read info payload"); + nbd_send_opt_abort(ioc); + return -1; + } + break; + } + } +} + /* Return -1 on failure, 0 if wantname is an available export. */ static int nbd_receive_query_exports(QIOChannel *ioc, const char *wantname, @@ -425,13 +600,13 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, } -int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, +int nbd_receive_negotiate(QIOChannel *ioc, const char *name, QCryptoTLSCreds *tlscreds, const char *hostname, - QIOChannel **outioc, - off_t *size, Error **errp) + QIOChannel **outioc, NBDExportInfo *info, + Error **errp) { char buf[256]; - uint64_t magic, s; + uint64_t magic; int rc; bool zeroes = true; @@ -515,11 +690,25 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, name = ""; } if (fixedNewStyle) { + int result; + + /* 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 + * NBD_OPT_LIST for nicer error messages about a missing + * export, then use NBD_OPT_EXPORT_NAME. */ + result = nbd_opt_go(ioc, name, info, errp); + if (result < 0) { + goto fail; + } + if (result > 0) { + return 0; + } /* Check our desired export is present in the * server export list. Since NBD_OPT_EXPORT_NAME * cannot return an error message, running this - * query gives us good error reporting if the - * server required TLS + * query gives us better error reporting if the + * export name is not available. */ if (nbd_receive_query_exports(ioc, name, errp) < 0) { goto fail; @@ -532,17 +721,17 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, } /* Read the response */ - if (nbd_read(ioc, &s, sizeof(s), errp) < 0) { + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { error_prepend(errp, "Failed to read export length"); goto fail; } - *size = be64_to_cpu(s); + be64_to_cpus(&info->size); - if (nbd_read(ioc, flags, sizeof(*flags), errp) < 0) { + if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { error_prepend(errp, "Failed to read export flags"); goto fail; } - be16_to_cpus(flags); + be16_to_cpus(&info->flags); } else if (magic == NBD_CLIENT_MAGIC) { uint32_t oldflags; @@ -555,11 +744,11 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, goto fail; } - if (nbd_read(ioc, &s, sizeof(s), errp) < 0) { + if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { error_prepend(errp, "Failed to read export length"); goto fail; } - *size = be64_to_cpu(s); + be64_to_cpus(&info->size); if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { error_prepend(errp, "Failed to read export flags"); @@ -570,13 +759,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); goto fail; } - *flags = oldflags; + info->flags = oldflags; } else { error_setg(errp, "Bad magic received"); goto fail; } - trace_nbd_receive_negotiate_size_flags(*size, *flags); + trace_nbd_receive_negotiate_size_flags(info->size, info->flags); if (zeroes && nbd_drop(ioc, 124, errp) < 0) { error_prepend(errp, "Failed to read reserved block"); goto fail; @@ -588,13 +777,19 @@ fail: } #ifdef __linux__ -int nbd_init(int fd, QIOChannelSocket *sioc, uint16_t flags, off_t size, +int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, Error **errp) { - unsigned long sectors = size / BDRV_SECTOR_SIZE; - if (size / BDRV_SECTOR_SIZE != sectors) { - error_setg(errp, "Export size %lld too large for 32-bit kernel", - (long long) size); + unsigned long sector_size = MAX(BDRV_SECTOR_SIZE, info->min_block); + unsigned long sectors = info->size / sector_size; + + /* FIXME: Once the kernel module is patched to honor block sizes, + * and to advertise that fact to user space, we should update the + * hand-off to the kernel to use any block sizes we learned. */ + assert(!info->request_sizes); + if (info->size / sector_size != sectors) { + error_setg(errp, "Export size %" PRIu64 " too large for 32-bit kernel", + info->size); return -E2BIG; } @@ -606,17 +801,17 @@ int nbd_init(int fd, QIOChannelSocket *sioc, uint16_t flags, off_t size, return -serrno; } - trace_nbd_init_set_block_size(BDRV_SECTOR_SIZE); + trace_nbd_init_set_block_size(sector_size); - if (ioctl(fd, NBD_SET_BLKSIZE, (unsigned long)BDRV_SECTOR_SIZE) < 0) { + if (ioctl(fd, NBD_SET_BLKSIZE, sector_size) < 0) { int serrno = errno; error_setg(errp, "Failed setting NBD block size"); return -serrno; } trace_nbd_init_set_size(sectors); - if (size % BDRV_SECTOR_SIZE) { - trace_nbd_init_trailing_bytes(size % BDRV_SECTOR_SIZE); + if (info->size % sector_size) { + trace_nbd_init_trailing_bytes(info->size % sector_size); } if (ioctl(fd, NBD_SET_SIZE_BLOCKS, sectors) < 0) { @@ -625,9 +820,9 @@ int nbd_init(int fd, QIOChannelSocket *sioc, uint16_t flags, off_t size, return -serrno; } - if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) flags) < 0) { + if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) info->flags) < 0) { if (errno == ENOTTY) { - int read_only = (flags & NBD_FLAG_READ_ONLY) != 0; + int read_only = (info->flags & NBD_FLAG_READ_ONLY) != 0; trace_nbd_init_set_readonly(); if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { @@ -685,7 +880,7 @@ int nbd_disconnect(int fd) } #else -int nbd_init(int fd, QIOChannelSocket *ioc, uint16_t flags, off_t size, +int nbd_init(int fd, QIOChannelSocket *ioc, NBDExportInfo *info, Error **errp) { error_setg(errp, "nbd_init is only supported on Linux"); diff --git a/nbd/common.c b/nbd/common.c index 4dab41e2c0..a2f28f2eec 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -101,3 +101,95 @@ void nbd_tls_handshake(QIOTask *task, data->complete = true; g_main_loop_quit(data->loop); } + + +const char *nbd_opt_lookup(uint32_t opt) +{ + switch (opt) { + case NBD_OPT_EXPORT_NAME: + return "export name"; + case NBD_OPT_ABORT: + return "abort"; + case NBD_OPT_LIST: + return "list"; + case NBD_OPT_STARTTLS: + return "starttls"; + case NBD_OPT_INFO: + return "info"; + case NBD_OPT_GO: + return "go"; + case NBD_OPT_STRUCTURED_REPLY: + return "structured reply"; + default: + return ""; + } +} + + +const char *nbd_rep_lookup(uint32_t rep) +{ + switch (rep) { + case NBD_REP_ACK: + return "ack"; + case NBD_REP_SERVER: + return "server"; + case NBD_REP_INFO: + return "info"; + case NBD_REP_ERR_UNSUP: + return "unsupported"; + case NBD_REP_ERR_POLICY: + return "denied by policy"; + case NBD_REP_ERR_INVALID: + return "invalid"; + case NBD_REP_ERR_PLATFORM: + return "platform lacks support"; + case NBD_REP_ERR_TLS_REQD: + return "TLS required"; + case NBD_REP_ERR_UNKNOWN: + return "export unknown"; + case NBD_REP_ERR_SHUTDOWN: + return "server shutting down"; + case NBD_REP_ERR_BLOCK_SIZE_REQD: + return "block size required"; + default: + return ""; + } +} + + +const char *nbd_info_lookup(uint16_t info) +{ + switch (info) { + case NBD_INFO_EXPORT: + return "export"; + case NBD_INFO_NAME: + return "name"; + case NBD_INFO_DESCRIPTION: + return "description"; + case NBD_INFO_BLOCK_SIZE: + return "block size"; + default: + return ""; + } +} + + +const char *nbd_cmd_lookup(uint16_t cmd) +{ + switch (cmd) { + case NBD_CMD_READ: + return "read"; + case NBD_CMD_WRITE: + return "write"; + case NBD_CMD_DISC: + return "discard"; + case NBD_CMD_FLUSH: + return "flush"; + case NBD_CMD_TRIM: + return "trim"; + case NBD_CMD_WRITE_ZEROES: + return "write zeroes"; + default: + return ""; + } +} diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index cf6ecbf358..4065bc68ac 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -37,8 +37,11 @@ * https://github.com/yoe/nbd/blob/master/doc/proto.md */ +/* Size of all NBD_OPT_*, without payload */ #define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4) +/* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */ #define NBD_REPLY_SIZE (4 + 4 + 8) + #define NBD_REQUEST_MAGIC 0x25609513 #define NBD_REPLY_MAGIC 0x67446698 #define NBD_OPTS_MAGIC 0x49484156454F5054LL @@ -57,12 +60,6 @@ #define NBD_SET_TIMEOUT _IO(0xab, 9) #define NBD_SET_FLAGS _IO(0xab, 10) -#define NBD_OPT_EXPORT_NAME (1) -#define NBD_OPT_ABORT (2) -#define NBD_OPT_LIST (3) -#define NBD_OPT_PEEK_EXPORT (4) -#define NBD_OPT_STARTTLS (5) - /* NBD errors are based on errno numbers, so there is a 1:1 mapping, * but only a limited set of errno values is specified in the protocol. * Everything else is squashed to EINVAL. @@ -133,6 +130,10 @@ struct NBDTLSHandshakeData { void nbd_tls_handshake(QIOTask *task, void *opaque); +const char *nbd_opt_lookup(uint32_t opt); +const char *nbd_rep_lookup(uint32_t rep); +const char *nbd_info_lookup(uint16_t info); +const char *nbd_cmd_lookup(uint16_t info); int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); diff --git a/nbd/server.c b/nbd/server.c index 9b0c588146..49ed57455c 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Red Hat, Inc. + * Copyright (C) 2016-2017 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device Server Side @@ -84,7 +84,6 @@ struct NBDClient { int refcount; void (*close_fn)(NBDClient *client, bool negotiated); - bool no_zeroes; NBDExport *exp; QCryptoTLSCreds *tlscreds; char *tlsaclname; @@ -139,8 +138,10 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type, { uint64_t magic; - trace_nbd_negotiate_send_rep_len(opt, type, len); + trace_nbd_negotiate_send_rep_len(opt, nbd_opt_lookup(opt), + type, nbd_rep_lookup(type), len); + assert(len < NBD_MAX_BUFFER_SIZE); magic = cpu_to_be64(NBD_REP_MAGIC); if (nbd_write(ioc, &magic, sizeof(magic), errp) < 0) { error_prepend(errp, "write failed (rep magic): "); @@ -275,10 +276,16 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length, return nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_LIST, errp); } +/* Send a reply to NBD_OPT_EXPORT_NAME. + * Return -errno on error, 0 on success. */ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length, + uint16_t myflags, bool no_zeroes, Error **errp) { char name[NBD_MAX_NAME_SIZE + 1]; + char buf[8 + 4 + 124] = ""; + size_t len; + int ret; /* Client sends: [20 .. xx] export name (length bytes) @@ -302,12 +309,219 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length, return -EINVAL; } + trace_nbd_negotiate_new_style_size_flags(client->exp->size, + client->exp->nbdflags | myflags); + stq_be_p(buf, client->exp->size); + stw_be_p(buf + 8, client->exp->nbdflags | myflags); + len = no_zeroes ? 10 : sizeof(buf); + ret = nbd_write(client->ioc, buf, len, errp); + if (ret < 0) { + error_prepend(errp, "write failed: "); + return ret; + } + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); return 0; } +/* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes. + * The buffer does NOT include the info type prefix. + * Return -errno on error, 0 if ready to send more. */ +static int nbd_negotiate_send_info(NBDClient *client, uint32_t opt, + uint16_t info, uint32_t length, void *buf, + Error **errp) +{ + int rc; + + trace_nbd_negotiate_send_info(info, nbd_info_lookup(info), length); + rc = nbd_negotiate_send_rep_len(client->ioc, NBD_REP_INFO, opt, + sizeof(info) + length, errp); + if (rc < 0) { + return rc; + } + cpu_to_be16s(&info); + if (nbd_write(client->ioc, &info, sizeof(info), errp) < 0) { + return -EIO; + } + if (nbd_write(client->ioc, buf, length, errp) < 0) { + return -EIO; + } + return 0; +} + +/* Handle NBD_OPT_INFO and NBD_OPT_GO. + * Return -errno on error, 0 if ready for next option, and 1 to move + * into transmission phase. */ +static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length, + uint32_t opt, uint16_t myflags, + Error **errp) +{ + int rc; + char name[NBD_MAX_NAME_SIZE + 1]; + NBDExport *exp; + uint16_t requests; + uint16_t request; + uint32_t namelen; + bool sendname = false; + bool blocksize = false; + uint32_t sizes[3]; + char buf[sizeof(uint64_t) + sizeof(uint16_t)]; + const char *msg; + + /* Client sends: + 4 bytes: L, name length (can be 0) + L bytes: export name + 2 bytes: N, number of requests (can be 0) + N * 2 bytes: N requests + */ + if (length < sizeof(namelen) + sizeof(requests)) { + msg = "overall request too short"; + goto invalid; + } + if (nbd_read(client->ioc, &namelen, sizeof(namelen), errp) < 0) { + return -EIO; + } + be32_to_cpus(&namelen); + length -= sizeof(namelen); + if (namelen > length - sizeof(requests) || (length - namelen) % 2) { + msg = "name length is incorrect"; + goto invalid; + } + if (nbd_read(client->ioc, name, namelen, errp) < 0) { + return -EIO; + } + name[namelen] = '\0'; + length -= namelen; + trace_nbd_negotiate_handle_export_name_request(name); + + if (nbd_read(client->ioc, &requests, sizeof(requests), errp) < 0) { + return -EIO; + } + be16_to_cpus(&requests); + length -= sizeof(requests); + trace_nbd_negotiate_handle_info_requests(requests); + if (requests != length / sizeof(request)) { + msg = "incorrect number of requests for overall length"; + goto invalid; + } + while (requests--) { + if (nbd_read(client->ioc, &request, sizeof(request), errp) < 0) { + return -EIO; + } + be16_to_cpus(&request); + length -= sizeof(request); + trace_nbd_negotiate_handle_info_request(request, + nbd_info_lookup(request)); + /* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE; + * everything else is either a request we don't know or + * something we send regardless of request */ + switch (request) { + case NBD_INFO_NAME: + sendname = true; + break; + case NBD_INFO_BLOCK_SIZE: + blocksize = true; + break; + } + } + + exp = nbd_export_find(name); + if (!exp) { + return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_UNKNOWN, + opt, errp, "export '%s' not present", + name); + } + + /* Don't bother sending NBD_INFO_NAME unless client requested it */ + if (sendname) { + rc = nbd_negotiate_send_info(client, opt, NBD_INFO_NAME, length, name, + errp); + if (rc < 0) { + return rc; + } + } + + /* Send NBD_INFO_DESCRIPTION only if available, regardless of + * client request */ + if (exp->description) { + size_t len = strlen(exp->description); + + rc = nbd_negotiate_send_info(client, opt, NBD_INFO_DESCRIPTION, + len, exp->description, errp); + if (rc < 0) { + return rc; + } + } + + /* Send NBD_INFO_BLOCK_SIZE always, but tweak the minimum size + * according to whether the client requested it, and according to + * whether this is OPT_INFO or OPT_GO. */ + /* minimum - 1 for back-compat, or 512 if client is new enough. + * TODO: consult blk_bs(blk)->bl.request_alignment? */ + sizes[0] = (opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1; + /* preferred - Hard-code to 4096 for now. + * TODO: is blk_bs(blk)->bl.opt_transfer appropriate? */ + sizes[1] = 4096; + /* maximum - At most 32M, but smaller as appropriate. */ + sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE); + trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]); + cpu_to_be32s(&sizes[0]); + cpu_to_be32s(&sizes[1]); + cpu_to_be32s(&sizes[2]); + rc = nbd_negotiate_send_info(client, opt, NBD_INFO_BLOCK_SIZE, + sizeof(sizes), sizes, errp); + if (rc < 0) { + return rc; + } + + /* Send NBD_INFO_EXPORT always */ + trace_nbd_negotiate_new_style_size_flags(exp->size, + exp->nbdflags | myflags); + stq_be_p(buf, exp->size); + stw_be_p(buf + 8, exp->nbdflags | myflags); + rc = nbd_negotiate_send_info(client, opt, NBD_INFO_EXPORT, + sizeof(buf), buf, errp); + if (rc < 0) { + return rc; + } + + /* If the client is just asking for NBD_OPT_INFO, but forgot to + * request block sizes, return an error. + * TODO: consult blk_bs(blk)->request_align, and only error if it + * is not 1? */ + if (opt == NBD_OPT_INFO && !blocksize) { + return nbd_negotiate_send_rep_err(client->ioc, + NBD_REP_ERR_BLOCK_SIZE_REQD, opt, + errp, + "request NBD_INFO_BLOCK_SIZE to " + "use this export"); + } + + /* Final reply */ + rc = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, opt, errp); + if (rc < 0) { + return rc; + } + + if (opt == NBD_OPT_GO) { + client->exp = exp; + QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); + nbd_export_get(client->exp); + rc = 1; + } + return rc; + + invalid: + if (nbd_drop(client->ioc, length, errp) < 0) { + return -EIO; + } + return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_INVALID, opt, + errp, "%s", msg); +} + + /* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the * new channel for all further (now-encrypted) communication. */ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, @@ -365,22 +579,25 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, } /* nbd_negotiate_options - * Process all NBD_OPT_* client option commands. + * Process all NBD_OPT_* client option commands, during fixed newstyle + * negotiation. * Return: * -errno on error, errp is set * 0 on successful negotiation, errp is not set * 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect, * errp is not set */ -static int nbd_negotiate_options(NBDClient *client, Error **errp) +static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, + Error **errp) { uint32_t flags; bool fixedNewstyle = false; - Error *local_err = NULL; + bool no_zeroes = false; /* Client sends: [ 0 .. 3] client flags + Then we loop until NBD_OPT_EXPORT_NAME or NBD_OPT_GO: [ 0 .. 7] NBD_OPTS_MAGIC [ 8 .. 11] NBD option [12 .. 15] Data length @@ -396,21 +613,19 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) error_prepend(errp, "read failed: "); return -EIO; } - trace_nbd_negotiate_options_flags(); be32_to_cpus(&flags); + trace_nbd_negotiate_options_flags(flags); if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { - trace_nbd_negotiate_options_newstyle(); fixedNewstyle = true; flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE; } if (flags & NBD_FLAG_C_NO_ZEROES) { - trace_nbd_negotiate_options_no_zeroes(); - client->no_zeroes = true; + no_zeroes = true; flags &= ~NBD_FLAG_C_NO_ZEROES; } if (flags != 0) { error_setg(errp, "Unknown client flags 0x%" PRIx32 " received", flags); - return -EIO; + return -EINVAL; } while (1) { @@ -442,7 +657,8 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) } length = be32_to_cpu(length); - trace_nbd_negotiate_options_check_option(option); + trace_nbd_negotiate_options_check_option(option, + nbd_opt_lookup(option)); if (client->tlscreds && client->ioc == (QIOChannel *)client->sioc) { QIOChannel *tioc; @@ -479,7 +695,9 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) if (ret < 0) { return ret; } - /* Let the client keep trying, unless they asked to quit */ + /* Let the client keep trying, unless they asked to + * quit. In this mode, we've already sent an error, so + * we can't ack the abort. */ if (option == NBD_OPT_ABORT) { return 1; } @@ -498,19 +716,26 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) /* NBD spec says we must try to reply before * disconnecting, but that we must also tolerate * guests that don't wait for our reply. */ - nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, option, - &local_err); - - if (local_err != NULL) { - const char *error = error_get_pretty(local_err); - trace_nbd_opt_abort_reply_failed(error); - error_free(local_err); - } - + nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, option, NULL); return 1; case NBD_OPT_EXPORT_NAME: - return nbd_negotiate_handle_export_name(client, length, errp); + return nbd_negotiate_handle_export_name(client, length, + myflags, no_zeroes, + errp); + + case NBD_OPT_INFO: + case NBD_OPT_GO: + ret = nbd_negotiate_handle_info(client, length, option, + myflags, errp); + if (ret == 1) { + assert(option == NBD_OPT_GO); + return 0; + } + if (ret) { + return ret; + } + break; case NBD_OPT_STARTTLS: if (nbd_drop(client->ioc, length, errp) < 0) { @@ -539,8 +764,8 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) NBD_REP_ERR_UNSUP, option, errp, "Unsupported option 0x%" - PRIx32, - option); + PRIx32 " (%s)", option, + nbd_opt_lookup(option)); if (ret < 0) { return ret; } @@ -553,10 +778,13 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) */ switch (option) { case NBD_OPT_EXPORT_NAME: - return nbd_negotiate_handle_export_name(client, length, errp); + return nbd_negotiate_handle_export_name(client, length, + myflags, no_zeroes, + errp); default: - error_setg(errp, "Unsupported option 0x%" PRIx32, option); + error_setg(errp, "Unsupported option 0x%" PRIx32 " (%s)", + option, nbd_opt_lookup(option)); return -EINVAL; } } @@ -578,7 +806,6 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | NBD_FLAG_SEND_WRITE_ZEROES); bool oldStyle; - size_t len; /* Old style negotiation header without options [ 0 .. 7] passwd ("NBDMAGIC") @@ -592,10 +819,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) [ 0 .. 7] passwd ("NBDMAGIC") [ 8 .. 15] magic (NBD_OPTS_MAGIC) [16 .. 17] server flags (0) - ....options sent.... - [18 .. 25] size - [26 .. 27] export flags - [28 .. 151] reserved (0, omit if no_zeroes) + ....options sent, ending in NBD_OPT_EXPORT_NAME or NBD_OPT_GO.... */ qio_channel_set_blocking(client->ioc, false, NULL); @@ -624,24 +848,13 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) error_prepend(errp, "write failed: "); return -EINVAL; } - ret = nbd_negotiate_options(client, errp); + ret = nbd_negotiate_options(client, myflags, errp); if (ret != 0) { if (ret < 0) { error_prepend(errp, "option negotiation failed: "); } return ret; } - - trace_nbd_negotiate_new_style_size_flags( - client->exp->size, client->exp->nbdflags | myflags); - stq_be_p(buf + 18, client->exp->size); - stw_be_p(buf + 26, client->exp->nbdflags | myflags); - len = client->no_zeroes ? 10 : sizeof(buf) - 18; - ret = nbd_write(client->ioc, buf + 18, len, errp); - if (ret < 0) { - error_prepend(errp, "write failed: "); - return ret; - } } trace_nbd_negotiate_success(); @@ -1040,7 +1253,8 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, return -EIO; } - trace_nbd_co_receive_request_decode_type(request->handle, request->type); + trace_nbd_co_receive_request_decode_type(request->handle, request->type, + nbd_cmd_lookup(request->type)); if (request->type != NBD_CMD_WRITE) { /* No payload, we are ready to read the next request. */ diff --git a/nbd/trace-events b/nbd/trace-events index 4b233b8510..f5024d85a1 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -1,8 +1,12 @@ # nbd/client.c nbd_unknown_error(int err) "Squashing unexpected error %d to EINVAL" -nbd_send_option_request(uint32_t opt, uint32_t len) "Sending option request %" PRIu32", len %" PRIu32 -nbd_receive_option_reply(uint32_t option, uint32_t type, uint32_t length) "Received option reply %" PRIx32", type %" PRIx32", len %" PRIu32 -nbd_reply_err_unsup(uint32_t option) "server doesn't understand request %" PRIx32 ", attempting fallback" +nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending option request %" PRIu32" (%s), len %" PRIu32 +nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIx32" (%s), type %" PRIx32" (%s), len %" PRIu32 +nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIx32 " (%s), attempting fallback" +nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'" +nbd_opt_go_success(void) "Export is good to go" +nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" +nbd_opt_go_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'" nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" nbd_receive_starttls_request(void) "Requesting TLS from server" @@ -28,19 +32,20 @@ nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, u nbd_receive_reply(uint32_t magic, int32_t error, uint64_t handle) "Got reply: { magic = 0x%" PRIx32 ", .error = % " PRId32 ", handle = %" PRIu64" }" # nbd/server.c -nbd_negotiate_send_rep_len(uint32_t opt, uint32_t type, uint32_t len) "Reply opt=%" PRIx32 " type=%" PRIx32 " len=%" PRIu32 +nbd_negotiate_send_rep_len(uint32_t opt, const char *optname, uint32_t type, const char *typename, uint32_t len) "Reply opt=%" PRIx32 " (%s), type=%" PRIx32 " (%s), len=%" PRIu32 nbd_negotiate_send_rep_err(const char *msg) "sending error message \"%s\"" nbd_negotiate_send_rep_list(const char *name, const char *desc) "Advertising export name '%s' description '%s'" nbd_negotiate_handle_export_name(void) "Checking length" nbd_negotiate_handle_export_name_request(const char *name) "Client requested export '%s'" +nbd_negotiate_send_info(int info, const char *name, uint32_t length) "Sending NBD_REP_INFO type %d (%s) with remaining length %" PRIu32 +nbd_negotiate_handle_info_requests(int requests) "Client requested %d items of info" +nbd_negotiate_handle_info_request(int request, const char *name) "Client requested info %d (%s)" +nbd_negotiate_handle_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "advertising minimum 0x%" PRIx32 ", preferred 0x%" PRIx32 ", maximum 0x%" PRIx32 nbd_negotiate_handle_starttls(void) "Setting up TLS" nbd_negotiate_handle_starttls_handshake(void) "Starting TLS handshake" -nbd_negotiate_options_flags(void) "Checking client flags" -nbd_negotiate_options_newstyle(void) "Client supports fixed newstyle handshake" -nbd_negotiate_options_no_zeroes(void) "Client supports no zeroes at handshake end" +nbd_negotiate_options_flags(uint32_t flags) "Received client flags 0x%" PRIx32 nbd_negotiate_options_check_magic(uint64_t magic) "Checking opts magic 0x%" PRIx64 -nbd_negotiate_options_check_option(uint32_t option) "Checking option 0x%" PRIx32 -nbd_opt_abort_reply_failed(const char *error) "Reply to NBD_OPT_ABORT request failed: %s" +nbd_negotiate_options_check_option(uint32_t option, const char *name) "Checking option 0x%" PRIx32 " (%s)" nbd_negotiate_begin(void) "Beginning negotiation" nbd_negotiate_old_style(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags %x" nbd_negotiate_new_style_size_flags(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags %x" @@ -50,7 +55,7 @@ nbd_send_reply(int32_t error, uint64_t handle) "Sending response to client: { .e nbd_blk_aio_attached(const char *name, void *ctx) "Export %s: Attaching clients to AIO context %p\n" nbd_blk_aio_detach(const char *name, void *ctx) "Export %s: Detaching clients from AIO context %p\n" nbd_co_send_reply(uint64_t handle, uint32_t error, int len) "Send reply: handle = %" PRIu64 ", error = %" PRIu32 ", len = %d" -nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 +nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 nbd_co_receive_request_cmd_write(uint32_t len) "Reading %" PRIu32 " byte(s)" nbd_trip(void) "Reading request" diff --git a/net/colo-compare.c b/net/colo-compare.c index 6d500e1dc4..abfc23ce80 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -543,7 +543,7 @@ static void compare_pri_chr_in(void *opaque, const uint8_t *buf, int size) ret = net_fill_rstate(&s->pri_rs, buf, size); if (ret == -1) { - qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, + qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, NULL, NULL, NULL, true); error_report("colo-compare primary_in error"); } @@ -560,7 +560,7 @@ static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size) ret = net_fill_rstate(&s->sec_rs, buf, size); if (ret == -1) { - qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, + qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, NULL, NULL, NULL, true); error_report("colo-compare secondary_in error"); } @@ -588,9 +588,11 @@ static void *colo_compare_thread(void *opaque) s->worker_context = g_main_context_new(); qemu_chr_fe_set_handlers(&s->chr_pri_in, compare_chr_can_read, - compare_pri_chr_in, NULL, s, s->worker_context, true); + compare_pri_chr_in, NULL, NULL, + s, s->worker_context, true); qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read, - compare_sec_chr_in, NULL, s, s->worker_context, true); + compare_sec_chr_in, NULL, NULL, + s, s->worker_context, true); s->compare_loop = g_main_loop_new(s->worker_context, FALSE); diff --git a/net/filter-mirror.c b/net/filter-mirror.c index 52d978fce2..6043549e5f 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -112,7 +112,7 @@ static void redirector_chr_read(void *opaque, const uint8_t *buf, int size) if (ret == -1) { qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, - NULL, NULL, true); + NULL, NULL, NULL, true); } } @@ -124,7 +124,7 @@ static void redirector_chr_event(void *opaque, int event) switch (event) { case CHR_EVENT_CLOSED: qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, - NULL, NULL, true); + NULL, NULL, NULL, true); break; default: break; @@ -163,7 +163,7 @@ static ssize_t filter_redirector_receive_iov(NetFilterState *nf, MirrorState *s = FILTER_REDIRECTOR(nf); int ret; - if (qemu_chr_fe_get_driver(&s->chr_out)) { + if (qemu_chr_fe_backend_connected(&s->chr_out)) { ret = filter_send(&s->chr_out, iov, iovcnt); if (ret) { error_report("filter redirector send failed(%s)", strerror(-ret)); @@ -245,7 +245,7 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp) qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read, redirector_chr_read, redirector_chr_event, - nf, NULL, true); + NULL, nf, NULL, true); } if (s->outdev) { diff --git a/net/slirp.c b/net/slirp.c index 6a6d727999..9fbc949e81 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -778,7 +778,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, fwd->slirp = s->slirp; qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read, - NULL, fwd, NULL, true); + NULL, NULL, fwd, NULL, true); } return 0; diff --git a/net/vhost-user.c b/net/vhost-user.c index a042ec6a34..36f32a2d84 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -211,7 +211,7 @@ static void chr_closed_bh(void *opaque) vhost_user_stop(queues, ncs); qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, - opaque, NULL, true); + NULL, opaque, NULL, true); if (err) { error_report_err(err); @@ -257,7 +257,7 @@ static void net_vhost_user_event(void *opaque, int event) g_source_remove(s->watch); s->watch = 0; - qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, NULL, NULL, false); aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque); @@ -305,7 +305,8 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, return -1; } qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, - net_vhost_user_event, nc0->name, NULL, true); + net_vhost_user_event, NULL, nc0->name, NULL, + true); } while (!s->started); assert(s->vhost_net); diff --git a/qapi-schema.json b/qapi-schema.json index 485767f1ab..ab438ead70 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -5089,6 +5089,46 @@ 'backend' : 'ChardevBackend' }, 'returns': 'ChardevReturn' } +## +# @chardev-change: +# +# Change a character device backend +# +# @id: the chardev's ID, must exist +# @backend: new backend type and parameters +# +# Returns: ChardevReturn. +# +# Since: 2.10 +# +# Example: +# +# -> { "execute" : "chardev-change", +# "arguments" : { "id" : "baz", +# "backend" : { "type" : "pty", "data" : {} } } } +# <- { "return": { "pty" : "/dev/pty/42" } } +# +# -> {"execute" : "chardev-change", +# "arguments" : { +# "id" : "charchannel2", +# "backend" : { +# "type" : "socket", +# "data" : { +# "addr" : { +# "type" : "unix" , +# "data" : { +# "path" : "/tmp/charchannel2.socket" +# } +# }, +# "server" : true, +# "wait" : false }}}} +# <- {"return": {}} +# +## +{ 'command': 'chardev-change', 'data': {'id' : 'str', + 'backend' : 'ChardevBackend' }, + 'returns': 'ChardevReturn' } + ## # @chardev-remove: # diff --git a/qemu-nbd.c b/qemu-nbd.c index 4dd3fd4732..78d05bea2d 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -255,8 +255,7 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { char *device = arg; - off_t size; - uint16_t nbdflags; + NBDExportInfo info = { .request_sizes = false, }; QIOChannelSocket *sioc; int fd; int ret; @@ -271,9 +270,8 @@ static void *nbd_client_thread(void *arg) goto out; } - ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags, - NULL, NULL, NULL, - &size, &local_error); + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, + NULL, NULL, NULL, &info, &local_error); if (ret < 0) { if (local_error) { error_report_err(local_error); @@ -288,7 +286,7 @@ static void *nbd_client_thread(void *arg) goto out_socket; } - ret = nbd_init(fd, sioc, nbdflags, size, &local_error); + ret = nbd_init(fd, sioc, &info, &local_error); if (ret < 0) { error_report_err(local_error); goto out_fd; diff --git a/qmp.c b/qmp.c index 84a4f29563..2cd40c3080 100644 --- a/qmp.c +++ b/qmp.c @@ -480,13 +480,14 @@ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass, * for removal. This conditional should be removed along with * it. */ - if (!prop->info->set) { + if (!prop->info->set && !prop->info->create) { return NULL; /* no way to set it, don't show */ } info = g_malloc0(sizeof(*info)); info->name = g_strdup(prop->name); - info->type = g_strdup(prop->info->name); + info->type = default_type ? g_strdup(default_type) + : g_strdup(prop->info->name); info->has_description = !!prop->info->description; info->description = g_strdup(prop->info->description); return info; diff --git a/qom/cpu.c b/qom/cpu.c index 8757f033a0..a39ff6c19c 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -458,6 +458,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; + dc->props = cpu_common_props; /* * Reason: CPUs still need special care by board code: wiring up * IRQs, adding reset handlers, halting non-first CPUs, ... diff --git a/qom/object.c b/qom/object.c index 5f6fdfa6e6..dfdbd50f04 100644 --- a/qom/object.c +++ b/qom/object.c @@ -740,7 +740,7 @@ out: return ret; } -const char *object_get_typename(Object *obj) +const char *object_get_typename(const Object *obj) { return obj->class->type->name; } @@ -1428,7 +1428,7 @@ out: g_free(type); } -void object_property_allow_set_link(Object *obj, const char *name, +void object_property_allow_set_link(const Object *obj, const char *name, Object *val, Error **errp) { /* Allow the link to be set, always */ @@ -1436,7 +1436,7 @@ void object_property_allow_set_link(Object *obj, const char *name, typedef struct { Object **child; - void (*check)(Object *, const char *, Object *, Error **); + void (*check)(const Object *, const char *, Object *, Error **); ObjectPropertyLinkFlags flags; } LinkProperty; @@ -1552,7 +1552,7 @@ static void object_release_link_property(Object *obj, const char *name, void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, - void (*check)(Object *, const char *, + void (*check)(const Object *, const char *, Object *, Error **), ObjectPropertyLinkFlags flags, Error **errp) diff --git a/qtest.c b/qtest.c index 9a5d1dc50d..88a09e9afc 100644 --- a/qtest.c +++ b/qtest.c @@ -691,7 +691,7 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) qemu_chr_fe_init(&qtest_chr, chr, errp); qemu_chr_fe_set_handlers(&qtest_chr, qtest_can_read, qtest_read, - qtest_event, &qtest_chr, NULL, true); + qtest_event, NULL, &qtest_chr, NULL, true); qemu_chr_fe_set_echo(&qtest_chr, true); inbuf = g_string_new(""); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index da942d91c7..4de91d5801 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1961,7 +1961,7 @@ static void x86_set_hv_spinlocks(Object *obj, Visitor *v, const char *name, cpu->hyperv_spinlock_attempts = value; } -static PropertyInfo qdev_prop_spinlocks = { +static const PropertyInfo qdev_prop_spinlocks = { .name = "int", .get = x86_get_hv_spinlocks, .set = x86_set_hv_spinlocks, diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index 783bf98217..ae25fafab9 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -8435,7 +8435,7 @@ static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, visit_type_null(v, name, NULL); } -static PropertyInfo ppc_compat_deprecated_propinfo = { +static const PropertyInfo ppc_compat_deprecated_propinfo = { .name = "str", .description = "compatibility mode (deprecated)", .get = getset_compat_deprecated, diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index 32e2bd7f1d..7aa1d1357b 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -158,7 +158,7 @@ void xtensa_sim_open_console(Chardev *chr) static CharBackend console; qemu_chr_fe_init(&console, chr, &error_abort); - qemu_chr_fe_set_handlers(&console, NULL, NULL, NULL, NULL, NULL, true); + qemu_chr_fe_set_handlers(&console, NULL, NULL, NULL, NULL, NULL, NULL, true); xtensa_sim_console = &console; } diff --git a/tests/test-char.c b/tests/test-char.c index b962063e56..7ac25ff73f 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -178,6 +178,7 @@ static void char_mux_test(void) fe_can_read, fe_read, fe_event, + NULL, &h1, NULL, true); @@ -186,6 +187,7 @@ static void char_mux_test(void) fe_can_read, fe_read, fe_event, + NULL, &h2, NULL, true); qemu_chr_fe_take_focus(&chr_be2); @@ -209,7 +211,8 @@ static void char_mux_test(void) h1.read_count = 0; /* remove first handler */ - qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, true); + qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, + NULL, NULL, true); qemu_chr_be_write(base, (void *)"hello", 6); g_assert_cmpint(h1.read_count, ==, 0); g_assert_cmpint(h2.read_count, ==, 0); @@ -307,13 +310,13 @@ static void char_socket_test(void) qemu_chr_fe_init(&be, chr, &error_abort); qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read, - NULL, &d, NULL, true); + NULL, NULL, &d, NULL, true); chr_client = qemu_chr_new("client", tmp); qemu_chr_fe_init(&client_be, chr_client, &error_abort); qemu_chr_fe_set_handlers(&client_be, socket_can_read_hello, socket_read_hello, - NULL, &d, NULL, true); + NULL, NULL, &d, NULL, true); g_free(tmp); d.conn_expected = true; @@ -383,6 +386,7 @@ static void char_pipe_test(void) fe_can_read, fe_read, fe_event, + NULL, &fe, NULL, true); @@ -403,16 +407,11 @@ static void char_pipe_test(void) } #endif -static void char_udp_test(void) +static int make_udp_socket(int *port) { - struct sockaddr_in addr = { 0, }, other; - SocketIdleData d = { 0, }; - Chardev *chr; - CharBackend be; + struct sockaddr_in addr = { 0, }; socklen_t alen = sizeof(addr); int ret, sock = qemu_socket(PF_INET, SOCK_DGRAM, 0); - char buf[10]; - char *tmp; g_assert_cmpint(sock, >, 0); addr.sin_family = AF_INET ; @@ -423,19 +422,41 @@ static void char_udp_test(void) ret = getsockname(sock, (struct sockaddr *)&addr, &alen); g_assert_cmpint(ret, ==, 0); - tmp = g_strdup_printf("udp:127.0.0.1:%d", - ntohs(addr.sin_port)); - chr = qemu_chr_new("client", tmp); - g_assert_nonnull(chr); + *port = ntohs(addr.sin_port); + return sock; +} + +static void char_udp_test_internal(Chardev *reuse_chr, int sock) +{ + struct sockaddr_in other; + SocketIdleData d = { 0, }; + Chardev *chr; + CharBackend *be; + socklen_t alen = sizeof(other); + int ret; + char buf[10]; + char *tmp = NULL; + + if (reuse_chr) { + chr = reuse_chr; + be = chr->be; + } else { + int port; + sock = make_udp_socket(&port); + tmp = g_strdup_printf("udp:127.0.0.1:%d", port); + chr = qemu_chr_new("client", tmp); + g_assert_nonnull(chr); + + be = g_alloca(sizeof(CharBackend)); + qemu_chr_fe_init(be, chr, &error_abort); + } d.chr = chr; - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_handlers(&be, socket_can_read_hello, socket_read_hello, - NULL, &d, NULL, true); + qemu_chr_fe_set_handlers(be, socket_can_read_hello, socket_read_hello, + NULL, NULL, &d, NULL, true); ret = qemu_chr_write_all(chr, (uint8_t *)"hello", 5); g_assert_cmpint(ret, ==, 5); - alen = sizeof(addr); ret = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&other, &alen); g_assert_cmpint(ret, ==, 5); @@ -444,10 +465,18 @@ static void char_udp_test(void) main_loop(); - close(sock); + if (!reuse_chr) { + close(sock); + qemu_chr_fe_deinit(be, true); + } g_free(tmp); } +static void char_udp_test(void) +{ + char_udp_test_internal(NULL, 0); +} + #ifdef HAVE_CHARDEV_SERIAL static void char_serial_test(void) { @@ -474,81 +503,110 @@ static void char_serial_test(void) } #endif -static void char_file_test(void) +#ifndef _WIN32 +static void char_file_fifo_test(void) { + Chardev *chr; + CharBackend be; char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *fifo = g_build_filename(tmp_path, "fifo", NULL); char *out = g_build_filename(tmp_path, "out", NULL); - char *contents = NULL; - ChardevFile file = { .out = out }; + ChardevFile file = { .in = fifo, + .has_in = true, + .out = out }; ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, .u.file.data = &file }; + FeHandler fe = { 0, }; + int fd, ret; + + if (mkfifo(fifo, 0600) < 0) { + abort(); + } + + fd = open(fifo, O_RDWR); + ret = write(fd, "fifo-in", 8); + g_assert_cmpint(ret, ==, 8); + + chr = qemu_chardev_new("label-file", TYPE_CHARDEV_FILE, &backend, + &error_abort); + + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_set_handlers(&be, + fe_can_read, + fe_read, + fe_event, + NULL, + &fe, NULL, true); + + g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); + qmp_chardev_send_break("label-foo", NULL); + g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); + qmp_chardev_send_break("label-file", NULL); + g_assert_cmpint(fe.last_event, ==, CHR_EVENT_BREAK); + + main_loop(); + + close(fd); + + g_assert_cmpint(fe.read_count, ==, 8); + g_assert_cmpstr(fe.read_buf, ==, "fifo-in"); + + qemu_chr_fe_deinit(&be, true); + + g_unlink(fifo); + g_free(fifo); + g_unlink(out); + g_free(out); + g_rmdir(tmp_path); + g_free(tmp_path); +} +#endif + +static void char_file_test_internal(Chardev *ext_chr, const char *filepath) +{ + char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *out; Chardev *chr; + char *contents = NULL; + ChardevFile file = {}; + ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, + .u.file.data = &file }; gsize length; int ret; - chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend, - &error_abort); + if (ext_chr) { + chr = ext_chr; + out = g_strdup(filepath); + file.out = out; + } else { + out = g_build_filename(tmp_path, "out", NULL); + file.out = out; + chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend, + &error_abort); + } ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6); g_assert_cmpint(ret, ==, 6); - object_unref(OBJECT(chr)); ret = g_file_get_contents(out, &contents, &length, NULL); g_assert(ret == TRUE); g_assert_cmpint(length, ==, 6); g_assert(strncmp(contents, "hello!", 6) == 0); - g_free(contents); -#ifndef _WIN32 - { - CharBackend be; - FeHandler fe = { 0, }; - char *fifo = g_build_filename(tmp_path, "fifo", NULL); - int fd; - - if (mkfifo(fifo, 0600) < 0) { - abort(); - } - - fd = open(fifo, O_RDWR); - ret = write(fd, "fifo-in", 8); - g_assert_cmpint(ret, ==, 8); - - file.in = fifo; - file.has_in = true; - chr = qemu_chardev_new("label-file", TYPE_CHARDEV_FILE, &backend, - &error_abort); - - qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_handlers(&be, - fe_can_read, - fe_read, - fe_event, - &fe, NULL, true); - - g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); - qmp_chardev_send_break("label-foo", NULL); - g_assert_cmpint(fe.last_event, !=, CHR_EVENT_BREAK); - qmp_chardev_send_break("label-file", NULL); - g_assert_cmpint(fe.last_event, ==, CHR_EVENT_BREAK); - - main_loop(); - - close(fd); - - g_assert_cmpint(fe.read_count, ==, 8); - g_assert_cmpstr(fe.read_buf, ==, "fifo-in"); - qemu_chr_fe_deinit(&be, true); - g_unlink(fifo); - g_free(fifo); + if (!ext_chr) { + object_unref(OBJECT(chr)); + g_unlink(out); } -#endif - - g_unlink(out); + g_free(contents); g_rmdir(tmp_path); g_free(tmp_path); g_free(out); } +static void char_file_test(void) +{ + char_file_test_internal(NULL, NULL); +} + static void char_null_test(void) { Error *err = NULL; @@ -583,6 +641,7 @@ static void char_null_test(void) fe_can_read, fe_read, fe_event, + NULL, NULL, NULL, true); ret = qemu_chr_fe_write(&be, (void *)"buf", 4); @@ -599,6 +658,76 @@ static void char_invalid_test(void) g_assert_null(chr); } +static int chardev_change(void *opaque) +{ + return 0; +} + +static int chardev_change_denied(void *opaque) +{ + return -1; +} + +static void char_hotswap_test(void) +{ + char *chr_args; + Chardev *chr; + CharBackend be; + + gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + char *filename = g_build_filename(tmp_path, "file", NULL); + ChardevFile file = { .out = filename }; + ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE, + .u.file.data = &file }; + ChardevReturn *ret; + + int port; + int sock = make_udp_socket(&port); + g_assert_cmpint(sock, >, 0); + + chr_args = g_strdup_printf("udp:127.0.0.1:%d", port); + + chr = qemu_chr_new("chardev", chr_args); + qemu_chr_fe_init(&be, chr, &error_abort); + + /* check that chardev operates correctly */ + char_udp_test_internal(chr, sock); + + /* set the handler that denies the hotswap */ + qemu_chr_fe_set_handlers(&be, NULL, NULL, + NULL, chardev_change_denied, NULL, NULL, true); + + /* now, change is denied and has to keep the old backend operating */ + ret = qmp_chardev_change("chardev", &backend, NULL); + g_assert(!ret); + g_assert(be.chr == chr); + + char_udp_test_internal(chr, sock); + + /* now allow the change */ + qemu_chr_fe_set_handlers(&be, NULL, NULL, + NULL, chardev_change, NULL, NULL, true); + + /* has to succeed now */ + ret = qmp_chardev_change("chardev", &backend, &error_abort); + g_assert(be.chr != chr); + + close(sock); + chr = be.chr; + + /* run the file chardev test */ + char_file_test_internal(chr, filename); + + object_unparent(OBJECT(chr)); + + qapi_free_ChardevReturn(ret); + g_unlink(filename); + g_free(filename); + g_rmdir(tmp_path); + g_free(tmp_path); + g_free(chr_args); +} + int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); @@ -625,11 +754,15 @@ int main(int argc, char **argv) g_test_add_func("/char/pipe", char_pipe_test); #endif g_test_add_func("/char/file", char_file_test); +#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/udp", char_udp_test); #ifdef HAVE_CHARDEV_SERIAL g_test_add_func("/char/serial", char_serial_test); #endif + g_test_add_func("/char/hotswap", char_hotswap_test); return g_test_run(); } diff --git a/tests/test-hmp.c b/tests/test-hmp.c index 6dfa0c36e2..d77b3c8710 100644 --- a/tests/test-hmp.c +++ b/tests/test-hmp.c @@ -23,6 +23,7 @@ static const char *hmp_cmds[] = { "boot_set ndc", "chardev-add null,id=testchardev1", "chardev-send-break testchardev2", + "chardev-change testchardev1 ringbuf", "chardev-remove testchardev1", "commit all", "cpu-add 1", diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index b3cc045765..d4da09f147 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -464,7 +464,7 @@ static void test_server_create_chr(TestServer *server, const gchar *opt) qemu_chr_fe_init(&server->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read, - chr_event, server, NULL, true); + chr_event, NULL, server, NULL, true); } static void test_server_listen(TestServer *server) diff --git a/vl.c b/vl.c index 8efd48bf9f..fb6b2efafa 100644 --- a/vl.c +++ b/vl.c @@ -3933,10 +3933,10 @@ int main(int argc, char **argv, char **envp) configure_rtc(opts); break; case QEMU_OPTION_tb_size: - if (!tcg_enabled()) { - error_report("TCG is disabled"); - exit(1); - } +#ifndef CONFIG_TCG + error_report("TCG is disabled"); + exit(1); +#endif if (qemu_strtoul(optarg, NULL, 0, &tcg_tb_size) < 0) { error_report("Invalid argument to -tb-size"); exit(1);