diff options
47 files changed, 681 insertions, 349 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 6fa36c7a2d..24b70169bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1351,6 +1351,7 @@ F: util/aio-*.c F: block/io.c F: migration/block* F: include/block/aio.h +F: include/block/aio-wait.h F: scripts/qemugdb/aio.py T: git git://github.com/stefanha/qemu.git block @@ -1 +1 @@ -2.11.90 +2.11.91 diff --git a/block/io.c b/block/io.c index 2b09c656d0..bd9a19a9c4 100644 --- a/block/io.c +++ b/block/io.c @@ -249,8 +249,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, BdrvCoDrainData data; /* Calling bdrv_drain() from a BH ensures the current coroutine yields and - * other coroutines run if they were queued from - * qemu_co_queue_run_restart(). */ + * other coroutines run if they were queued by aio_co_enter(). */ assert(qemu_in_coroutine()); data = (BdrvCoDrainData) { diff --git a/block/nbd-client.c b/block/nbd-client.c index e64e346d69..e7caf49fbb 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -239,7 +239,7 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, { uint32_t context_id; - if (chunk->length != sizeof(context_id) + sizeof(extent)) { + if (chunk->length != sizeof(context_id) + sizeof(*extent)) { error_setg(errp, "Protocol error: invalid payload for " "NBD_REPLY_TYPE_BLOCK_STATUS"); return -EINVAL; diff --git a/block/nvme.c b/block/nvme.c index 8bca57aae6..c4f3a7bc94 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -695,12 +695,11 @@ static void nvme_parse_filename(const char *filename, QDict *options, unsigned long ns; const char *slash = strchr(tmp, '/'); if (!slash) { - qdict_put(options, NVME_BLOCK_OPT_DEVICE, - qstring_from_str(tmp)); + qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, tmp); return; } device = g_strndup(tmp, slash - tmp); - qdict_put(options, NVME_BLOCK_OPT_DEVICE, qstring_from_str(device)); + qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, device); g_free(device); namespace = slash + 1; if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) { @@ -708,8 +707,8 @@ static void nvme_parse_filename(const char *filename, QDict *options, namespace); return; } - qdict_put(options, NVME_BLOCK_OPT_NAMESPACE, - qstring_from_str(*namespace ? namespace : "1")); + qdict_put_str(options, NVME_BLOCK_OPT_NAMESPACE, + *namespace ? namespace : "1"); } } @@ -1082,7 +1081,7 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) bs->drv->format_name); } - qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name)); + qdict_put_str(opts, "driver", bs->drv->format_name); bs->full_open_options = opts; } diff --git a/disas/riscv.c b/disas/riscv.c index 3c17501120..74ad16eacd 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -600,7 +600,7 @@ static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end }; static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end }; static const rvc_constraint rvcc_negw[] = { rvc_rs1_eq_x0, rvc_end }; -static const rvc_constraint rvcc_sext_w[] = { rvc_rs2_eq_x0, rvc_end }; +static const rvc_constraint rvcc_sext_w[] = { rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_seqz[] = { rvc_imm_eq_p1, rvc_end }; static const rvc_constraint rvcc_snez[] = { rvc_rs1_eq_x0, rvc_end }; static const rvc_constraint rvcc_sltz[] = { rvc_rs2_eq_x0, rvc_end }; @@ -2052,6 +2052,13 @@ int gdbserver_start(const char *device) return 0; } +void gdbserver_cleanup(void) +{ + if (gdbserver_state) { + put_packet(gdbserver_state, "W00"); + } +} + static void register_types(void) { type_register_static(&char_gdb_type_info); @@ -381,7 +381,7 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->cpu_index); - monitor_printf(mon, " thread-id=%" PRId64 "\n", cpu->value->thread_id); + monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id); } qapi_free_CpuInfoFastList(cpu_list); diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index ef8b80e9aa..a92dd50437 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -76,6 +76,8 @@ enum crb_cancel { CRB_CANCEL_INVOKE = BIT(0), }; +#define TPM_CRB_NO_LOCALITY 0xff + static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr, unsigned size) { @@ -95,10 +97,19 @@ static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr, return val; } +static uint8_t tpm_crb_get_active_locty(CRBState *s) +{ + if (!ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, locAssigned)) { + return TPM_CRB_NO_LOCALITY; + } + return ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, activeLocality); +} + static void tpm_crb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { CRBState *s = CRB(opaque); + uint8_t locty = addr >> 12; trace_tpm_crb_mmio_write(addr, size, val); @@ -123,7 +134,8 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr, break; case A_CRB_CTRL_START: if (val == CRB_START_INVOKE && - !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) { + !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) && + tpm_crb_get_active_locty(s) == locty) { void *mem = memory_region_get_ram_ptr(&s->cmdmem); s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE; @@ -145,6 +157,8 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr, case CRB_LOC_CTRL_RELINQUISH: ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, locAssigned, 0); + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, + Granted, 0); break; case CRB_LOC_CTRL_REQUEST_ACCESS: ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, @@ -220,6 +234,8 @@ static void tpm_crb_reset(void *dev) ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, tpmRegValidSts, 1); + ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, + tpmIdle, 1); ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE); ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 9aa7756d92..2e8a4b83b9 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -103,6 +103,8 @@ int gdbserver_start(int); int gdbserver_start(const char *port); #endif +void gdbserver_cleanup(void); + /** * gdb_has_xml: * This is an ugly hack to cope with both new and old gdb. diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 0cb0538a31..d6ab70cae2 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -13,6 +13,7 @@ extern Monitor *cur_mon; #define MONITOR_USE_READLINE 0x02 #define MONITOR_USE_CONTROL 0x04 #define MONITOR_USE_PRETTY 0x08 +#define MONITOR_USE_OOB 0x10 bool monitor_cur_is_qmp(void); diff --git a/include/qemu/coroutine_int.h b/include/qemu/coroutine_int.h index 59e8406398..bd6b0468e1 100644 --- a/include/qemu/coroutine_int.h +++ b/include/qemu/coroutine_int.h @@ -68,6 +68,5 @@ Coroutine *qemu_coroutine_new(void); void qemu_coroutine_delete(Coroutine *co); CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to, CoroutineAction action); -void coroutine_fn qemu_co_queue_run_restart(Coroutine *co); #endif diff --git a/include/qemu/queue.h b/include/qemu/queue.h index aa270d2b38..59fd1203a1 100644 --- a/include/qemu/queue.h +++ b/include/qemu/queue.h @@ -324,6 +324,14 @@ struct { \ } \ } while (/*CONSTCOND*/0) +#define QSIMPLEQ_PREPEND(head1, head2) do { \ + if (!QSIMPLEQ_EMPTY((head2))) { \ + *(head2)->sqh_last = (head1)->sqh_first; \ + (head1)->sqh_first = (head2)->sqh_first; \ + QSIMPLEQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + #define QSIMPLEQ_LAST(head, type, field) \ (QSIMPLEQ_EMPTY((head)) ? \ NULL : \ diff --git a/io/channel-websock.c b/io/channel-websock.c index ec48a305f0..e6608b969d 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -586,9 +586,7 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc, return TRUE; } - if (err) { - error_propagate(&wioc->io_err, err); - } + error_propagate(&wioc->io_err, err); trace_qio_channel_websock_handshake_reply(ioc); qio_channel_add_watch( diff --git a/migration/migration.c b/migration/migration.c index 52a5092add..58bd382730 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -306,13 +306,21 @@ static void process_incoming_migration_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; - /* Make sure all file formats flush their mutable metadata. - * If we get an error here, just don't restart the VM yet. */ - bdrv_invalidate_cache_all(&local_err); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - autostart = false; + /* Only fire up the block code now if we're going to restart the + * VM, else 'cont' will do it. + * This causes file locking to happen; so we don't want it to happen + * unless we really are starting the VM. + */ + if (autostart && (!global_state_received() || + global_state_get_runstate() == RUN_STATE_RUNNING)) { + /* Make sure all file formats flush their mutable metadata. + * If we get an error here, just don't restart the VM yet. */ + bdrv_invalidate_cache_all(&local_err); + if (local_err) { + error_report_err(local_err); + local_err = NULL; + autostart = false; + } } /* diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index efd77939af..4a0b33b373 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -754,6 +754,7 @@ static void *postcopy_ram_fault_thread(void *opaque) } } trace_postcopy_ram_fault_thread_exit(); + g_free(pfd); return NULL; } @@ -36,6 +36,7 @@ #include "net/slirp.h" #include "chardev/char-fe.h" #include "chardev/char-io.h" +#include "chardev/char-mux.h" #include "ui/qemu-spice.h" #include "sysemu/numa.h" #include "monitor/monitor.h" @@ -234,6 +235,22 @@ static struct { QEMUBH *qmp_respond_bh; } mon_global; +struct QMPRequest { + /* Owner of the request */ + Monitor *mon; + /* "id" field of the request */ + QObject *id; + /* Request object to be handled */ + QObject *req; + /* + * Whether we need to resume the monitor afterward. This flag is + * used to emulate the old QMP server behavior that the current + * command must be completed before execution of the next one. + */ + bool need_resume; +}; +typedef struct QMPRequest QMPRequest; + /* QMP checker flags */ #define QMP_ACCEPT_UNKNOWNS 1 @@ -310,6 +327,38 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +static void qmp_request_free(QMPRequest *req) +{ + qobject_decref(req->id); + qobject_decref(req->req); + g_free(req); +} + +/* Must with the mon->qmp.qmp_queue_lock held */ +static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) +{ + while (!g_queue_is_empty(mon->qmp.qmp_requests)) { + qmp_request_free(g_queue_pop_head(mon->qmp.qmp_requests)); + } +} + +/* Must with the mon->qmp.qmp_queue_lock held */ +static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon) +{ + while (!g_queue_is_empty(mon->qmp.qmp_responses)) { + qobject_decref(g_queue_pop_head(mon->qmp.qmp_responses)); + } +} + +static void monitor_qmp_cleanup_queues(Monitor *mon) +{ + qemu_mutex_lock(&mon->qmp.qmp_queue_lock); + monitor_qmp_cleanup_req_queue_locked(mon); + monitor_qmp_cleanup_resp_queue_locked(mon); + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); +} + + static void monitor_flush_locked(Monitor *mon); static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond, @@ -701,6 +750,8 @@ static void monitor_data_destroy(Monitor *mon) QDECREF(mon->outbuf); qemu_mutex_destroy(&mon->out_lock); qemu_mutex_destroy(&mon->qmp.qmp_queue_lock); + monitor_qmp_cleanup_req_queue_locked(mon); + monitor_qmp_cleanup_resp_queue_locked(mon); g_queue_free(mon->qmp.qmp_requests); g_queue_free(mon->qmp.qmp_responses); } @@ -1203,8 +1254,14 @@ static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp) cmd = qmp_find_command(mon->qmp.commands, command); if (!cmd) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, - "The command %s has not been found", command); + if (mon->qmp.commands == &qmp_cap_negotiation_commands) { + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "Expecting capabilities negotiation " + "with 'qmp_capabilities'"); + } else { + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "The command %s has not been found", command); + } return false; } @@ -4003,22 +4060,6 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp, qobject_decref(rsp); } -struct QMPRequest { - /* Owner of the request */ - Monitor *mon; - /* "id" field of the request */ - QObject *id; - /* Request object to be handled */ - QObject *req; - /* - * Whether we need to resume the monitor afterward. This flag is - * used to emulate the old QMP server behavior that the current - * command must be completed before execution of the next one. - */ - bool need_resume; -}; -typedef struct QMPRequest QMPRequest; - /* * Dispatch one single QMP request. The function will free the req_obj * and objects inside it before return. @@ -4027,7 +4068,6 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) { Monitor *mon, *old_mon; QObject *req, *rsp = NULL, *id; - QDict *qdict = NULL; bool need_resume; req = req_obj->req; @@ -4050,18 +4090,6 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj) cur_mon = old_mon; - if (mon->qmp.commands == &qmp_cap_negotiation_commands) { - qdict = qdict_get_qdict(qobject_to(QDict, rsp), "error"); - if (qdict - && !g_strcmp0(qdict_get_try_str(qdict, "class"), - QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) { - /* Provide a more useful error message */ - qdict_del(qdict, "desc"); - qdict_put_str(qdict, "desc", "Expecting capabilities negotiation" - " with 'qmp_capabilities'"); - } - } - /* Respond if necessary */ monitor_qmp_respond(mon, rsp, NULL, id); @@ -4198,9 +4226,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) qapi_event_send_command_dropped(id, COMMAND_DROP_REASON_QUEUE_FULL, &error_abort); - qobject_decref(id); - qobject_decref(req); - g_free(req_obj); + qmp_request_free(req_obj); return; } } @@ -4315,7 +4341,7 @@ static QObject *get_qmp_greeting(Monitor *mon) /* Monitors that are not using IOThread won't support OOB */ continue; } - qlist_append(cap_list, qstring_from_str(QMPCapability_str(cap))); + qlist_append_str(cap_list, QMPCapability_str(cap)); } return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}", @@ -4342,6 +4368,7 @@ static void monitor_qmp_event(void *opaque, int event) mon_refcount++; break; case CHR_EVENT_CLOSED: + monitor_qmp_cleanup_queues(mon); json_message_parser_destroy(&mon->qmp.parser); json_message_parser_init(&mon->qmp.parser, handle_qmp_command); mon_refcount--; @@ -4536,12 +4563,26 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) void monitor_init(Chardev *chr, int flags) { Monitor *mon = g_malloc(sizeof(*mon)); + bool use_readline = flags & MONITOR_USE_READLINE; + bool use_oob = flags & MONITOR_USE_OOB; + + if (use_oob) { + if (CHARDEV_IS_MUX(chr)) { + error_report("Monitor Out-Of-Band is not supported with " + "MUX typed chardev backend"); + exit(1); + } + if (use_readline) { + error_report("Monitor Out-Of-band is only supported by QMP"); + exit(1); + } + } - monitor_data_init(mon, false, false); + monitor_data_init(mon, false, use_oob); qemu_chr_fe_init(&mon->chr, chr, &error_abort); mon->flags = flags; - if (flags & MONITOR_USE_READLINE) { + if (use_readline) { mon->rs = readline_init(monitor_readline_printf, monitor_readline_flush, mon, @@ -4637,6 +4678,9 @@ QemuOptsList qemu_mon_opts = { },{ .name = "pretty", .type = QEMU_OPT_BOOL, + },{ + .name = "x-oob", + .type = QEMU_OPT_BOOL, }, { /* end of list */ } }, diff --git a/nbd/client.c b/nbd/client.c index 9b9b7f0ea2..b9e175d1c2 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -599,8 +599,8 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, * Set one meta context. Simple means that reply must contain zero (not * negotiated) or one (negotiated) contexts. More contexts would be considered * as a protocol error. It's also implied that meta-data query equals queried - * context name, so, if server replies with something different then @context, - * it considered as error too. + * context name, so, if server replies with something different than @context, + * it is considered an error too. * return 1 for successful negotiation, context_id is set * 0 if operation is unsupported, * -1 with errp set for any other error @@ -623,6 +623,7 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, char *data = g_malloc(data_len); char *p = data; + trace_nbd_opt_meta_request(context, export); stl_be_p(p, export_len); memcpy(p += sizeof(export_len), export, export_len); stl_be_p(p += export_len, 1); @@ -649,29 +650,38 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, if (reply.type == NBD_REP_META_CONTEXT) { char *name; - size_t len; + + if (reply.length != sizeof(received_id) + context_len) { + error_setg(errp, "Failed to negotiate meta context '%s', server " + "answered with unexpected length %" PRIu32, context, + reply.length); + nbd_send_opt_abort(ioc); + return -1; + } if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) { return -1; } be32_to_cpus(&received_id); - len = reply.length - sizeof(received_id); - name = g_malloc(len + 1); - if (nbd_read(ioc, name, len, errp) < 0) { + reply.length -= sizeof(received_id); + name = g_malloc(reply.length + 1); + if (nbd_read(ioc, name, reply.length, errp) < 0) { g_free(name); return -1; } - name[len] = '\0'; + name[reply.length] = '\0'; if (strcmp(context, name)) { error_setg(errp, "Failed to negotiate meta context '%s', server " "answered with different context '%s'", context, name); g_free(name); + nbd_send_opt_abort(ioc); return -1; } g_free(name); + trace_nbd_opt_meta_reply(context, received_id); received = true; /* receive NBD_REP_ACK */ @@ -690,6 +700,12 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, if (reply.type != NBD_REP_ACK) { error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", reply.type, NBD_REP_ACK); + nbd_send_opt_abort(ioc); + return -1; + } + if (reply.length) { + error_setg(errp, "Unexpected length to ACK response"); + nbd_send_opt_abort(ioc); return -1; } diff --git a/nbd/server.c b/nbd/server.c index cea158913b..9e1f227178 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -726,6 +726,7 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, context_id = 0; } + trace_nbd_negotiate_meta_query_reply(context, context_id); set_be_option_rep(&opt.h, client->opt, NBD_REP_META_CONTEXT, sizeof(opt) - sizeof(opt.h) + iov[1].iov_len); stl_be_p(&opt.context_id, context_id); @@ -752,10 +753,12 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, if (client->opt == NBD_OPT_LIST_META_CONTEXT) { meta->base_allocation = true; } + trace_nbd_negotiate_meta_query_parse("base:"); return 1; } if (len != alen) { + trace_nbd_negotiate_meta_query_skip("not base:allocation"); return nbd_opt_skip(client, len, errp); } @@ -768,6 +771,7 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, meta->base_allocation = true; } + trace_nbd_negotiate_meta_query_parse("base:allocation"); return 1; } @@ -800,6 +804,7 @@ static int nbd_negotiate_meta_query(NBDClient *client, /* The only supported namespace for now is 'base'. So query should start * with 'base:'. Otherwise, we can ignore it and skip the remainder. */ if (len < baselen) { + trace_nbd_negotiate_meta_query_skip("length too short"); return nbd_opt_skip(client, len, errp); } @@ -809,6 +814,7 @@ static int nbd_negotiate_meta_query(NBDClient *client, return ret; } if (strncmp(query, "base:", baselen) != 0) { + trace_nbd_negotiate_meta_query_skip("not for base: namespace"); return nbd_opt_skip(client, len, errp); } @@ -858,6 +864,8 @@ static int nbd_negotiate_meta_queries(NBDClient *client, return ret; } cpu_to_be32s(&nb_queries); + trace_nbd_negotiate_meta_context(nbd_opt_lookup(client->opt), + meta->export_name, nb_queries); if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) { /* enable all known contexts */ diff --git a/nbd/trace-events b/nbd/trace-events index 0d03edc967..dee081e775 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -10,6 +10,8 @@ nbd_receive_query_exports_start(const char *wantname) "Querying export list for nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" nbd_receive_starttls_new_client(void) "Setting up TLS" nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake" +nbd_opt_meta_request(const char *context, const char *export) "Requesting to set meta context %s for export %s" +nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32 nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64 nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32 @@ -44,6 +46,10 @@ nbd_negotiate_handle_info_request(int request, const char *name) "Client request 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_meta_context(const char *optname, const char *export, uint32_t queries) "Client requested %s for export %s, with %" PRIu32 " queries" +nbd_negotiate_meta_query_skip(const char *reason) "Skipping meta query: %s" +nbd_negotiate_meta_query_parse(const char *query) "Parsed meta query '%s'" +nbd_negotiate_meta_query_reply(const char *context, uint32_t id) "Replying with meta context '%s' id %" PRIu32 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, const char *name) "Checking option %" PRIu32 " (%s)" diff --git a/qemu-options.hx b/qemu-options.hx index 74158e7493..3ece30d216 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2048,41 +2048,40 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " old way to initialize a host network interface\n" " (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL) STEXI -@item -net nic[,vlan=@var{n}][,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] -@findex -net -Configure or create an on-board (or machine default) Network Interface Card -(NIC) and connect it either to VLAN @var{n} (@var{n} = 0 is the default), or -to the netdev @var{nd}. The NIC is an e1000 by default on the PC -target. Optionally, the MAC address can be changed to @var{mac}, the -device address set to @var{addr} (PCI cards only), -and a @var{name} can be assigned for use in monitor commands. -Optionally, for PCI cards, you can specify the number @var{v} of MSI-X vectors -that the card should have; this option currently only affects virtio cards; set -@var{v} = 0 to disable MSI-X. If no @option{-net} option is specified, a single -NIC is created. QEMU can emulate several different models of network card. -Valid values for @var{type} are -@code{virtio}, @code{i82551}, @code{i82557b}, @code{i82559er}, -@code{ne2k_pci}, @code{ne2k_isa}, @code{pcnet}, @code{rtl8139}, -@code{e1000}, @code{smc91c111}, @code{lance} and @code{mcf_fec}. -Not all devices are supported on all targets. Use @code{-net nic,model=help} -for a list of available devices for your target. +@item -nic [tap|bridge|user|l2tpv3|vde|netmap|vhost-user|socket][,...][,mac=macaddr][,model=mn] +@findex -nic +This option is a shortcut for configuring both the on-board (default) guest +NIC hardware and the host network backend in one go. The host backend options +are the same as with the corresponding @option{-netdev} options below. +The guest NIC model can be set with @option{model=@var{modelname}}. +Use @option{model=help} to list the available device types. +The hardware MAC address can be set with @option{mac=@var{macaddr}}. + +The following two example do exactly the same, to show how @option{-nic} can +be used to shorten the command line length (note that the e1000 is the default +on i386, so the @option{model=e1000} parameter could even be omitted here, too): +@example +qemu-system-i386 -netdev user,id=n1,ipv6=off -device e1000,netdev=n1,mac=52:54:98:76:54:32 +qemu-system-i386 -nic user,ipv6=off,model=e1000,mac=52:54:98:76:54:32 +@end example + +@item -nic none +Indicate that no network devices should be configured. It is used to override +the default configuration (default NIC with ``user'' host network backend) +which is activated if no other networking options are provided. @item -netdev user,id=@var{id}[,@var{option}][,@var{option}][,...] @findex -netdev -@item -net user[,@var{option}][,@var{option}][,...] -Use the user mode network stack which requires no administrator +Configure user mode host network backend which requires no administrator privilege to run. Valid options are: @table @option -@item vlan=@var{n} -Connect user mode stack to VLAN @var{n} (@var{n} = 0 is the default). - @item id=@var{id} -@itemx name=@var{name} Assign symbolic name for use in monitor commands. -@option{ipv4} and @option{ipv6} specify that either IPv4 or IPv6 must -be enabled. If neither is specified both protocols are enabled. +@item ipv4=on|off and ipv6=on|off +Specify that either IPv4 or IPv6 must be enabled. If neither is specified +both protocols are enabled. @item net=@var{addr}[/@var{mask}] Set IP network address the guest will see. Optionally specify the netmask, @@ -2134,7 +2133,7 @@ can not be resolved. Example: @example -qemu -net user,dnssearch=mgmt.example.org,dnssearch=example.org [...] +qemu-system-i386 -nic user,dnssearch=mgmt.example.org,dnssearch=example.org @end example @item tftp=@var{dir} @@ -2150,7 +2149,8 @@ a guest from a local directory. Example (using pxelinux): @example -qemu-system-i386 -hda linux.img -boot n -net user,tftp=/path/to/tftp/files,bootfile=/pxelinux.0 +qemu-system-i386 -hda linux.img -boot n -device e1000,netdev=n1 \ + -netdev user,id=n1,tftp=/path/to/tftp/files,bootfile=/pxelinux.0 @end example @item smb=@var{dir}[,smbserver=@var{addr}] @@ -2169,8 +2169,6 @@ or @file{C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS} (Windows NT/2000). Then @file{@var{dir}} can be accessed in @file{\\smbserver\qemu}. Note that a SAMBA server must be installed on the host OS. -QEMU was tested successfully with smbd versions from Red Hat 9, -Fedora Core 3 and OpenSUSE 11.x. @item hostfwd=[tcp|udp]:[@var{hostaddr}]:@var{hostport}-[@var{guestaddr}]:@var{guestport} Redirect incoming TCP or UDP connections to the host port @var{hostport} to @@ -2185,7 +2183,7 @@ screen 0, use the following: @example # on the host -qemu-system-i386 -net user,hostfwd=tcp:127.0.0.1:6001-:6000 [...] +qemu-system-i386 -nic user,hostfwd=tcp:127.0.0.1:6001-:6000 # this host xterm should open in the guest X11 server xterm -display :1 @end example @@ -2195,7 +2193,7 @@ the guest, use the following: @example # on the host -qemu-system-i386 -net user,hostfwd=tcp::5555-:23 [...] +qemu-system-i386 -nic user,hostfwd=tcp::5555-:23 telnet localhost 5555 @end example @@ -2214,7 +2212,7 @@ lifetime, like in the following example: @example # open 10.10.1.1:4321 on bootup, connect 10.0.2.100:1234 to it whenever # the guest accesses it -qemu -net user,guestfwd=tcp:10.0.2.100:1234-tcp:10.10.1.1:4321 [...] +qemu-system-i386 -nic user,guestfwd=tcp:10.0.2.100:1234-tcp:10.10.1.1:4321 @end example Or you can execute a command on every TCP connection established by the guest, @@ -2223,7 +2221,7 @@ so that QEMU behaves similar to an inetd process for that virtual server: @example # call "netcat 10.10.1.1 4321" on every TCP connection to 10.0.2.100:1234 # and connect the TCP stream to its stdin/stdout -qemu -net 'user,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10.1.1 4321' +qemu-system-i386 -nic 'user,id=n1,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10.1.1 4321' @end example @end table @@ -2234,8 +2232,7 @@ syntax gives undefined results. Their use for new applications is discouraged as they will be removed from future versions. @item -netdev tap,id=@var{id}[,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,br=@var{bridge}][,helper=@var{helper}] -@itemx -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,br=@var{bridge}][,helper=@var{helper}] -Connect the host TAP network interface @var{name} to VLAN @var{n}. +Configure a host TAP network backend with ID @var{id}. Use the network script @var{file} to configure it and the network script @var{dfile} to deconfigure it. If @var{name} is not provided, the OS @@ -2256,7 +2253,7 @@ Examples: @example #launch a QEMU instance with the default network script -qemu-system-i386 linux.img -net nic -net tap +qemu-system-i386 linux.img -nic tap @end example @example @@ -2270,12 +2267,11 @@ qemu-system-i386 linux.img \ @example #launch a QEMU instance with the default network helper to #connect a TAP device to bridge br0 -qemu-system-i386 linux.img \ - -net nic -net tap,"helper=/path/to/qemu-bridge-helper" +qemu-system-i386 linux.img -device virtio-net-pci,netdev=n1 \ + -netdev tap,id=n1,"helper=/path/to/qemu-bridge-helper" @end example @item -netdev bridge,id=@var{id}[,br=@var{bridge}][,helper=@var{helper}] -@itemx -net bridge[,vlan=@var{n}][,name=@var{name}][,br=@var{bridge}][,helper=@var{helper}] Connect a host TAP network interface to a host bridge device. Use the network helper @var{helper} to configure the TAP interface and @@ -2288,21 +2284,20 @@ Examples: @example #launch a QEMU instance with the default network helper to #connect a TAP device to bridge br0 -qemu-system-i386 linux.img -net bridge -net nic,model=virtio +qemu-system-i386 linux.img -netdev bridge,id=n1 -device virtio-net,netdev=n1 @end example @example #launch a QEMU instance with the default network helper to #connect a TAP device to bridge qemubr0 -qemu-system-i386 linux.img -net bridge,br=qemubr0 -net nic,model=virtio +qemu-system-i386 linux.img -netdev bridge,br=qemubr0,id=n1 -device virtio-net,netdev=n1 @end example @item -netdev socket,id=@var{id}[,fd=@var{h}][,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}] -@itemx -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}] [,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}] -Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual -machine using a TCP socket connection. If @option{listen} is -specified, QEMU waits for incoming connections on @var{port} +This host network backend can be used to connect the guest's network to +another QEMU virtual machine using a TCP socket connection. If @option{listen} +is specified, QEMU waits for incoming connections on @var{port} (@var{host} is optional). @option{connect} is used to connect to another QEMU instance using the @option{listen} option. @option{fd}=@var{h} specifies an already opened TCP socket. @@ -2311,21 +2306,19 @@ Example: @example # launch a first QEMU instance qemu-system-i386 linux.img \ - -net nic,macaddr=52:54:00:12:34:56 \ - -net socket,listen=:1234 -# connect the VLAN 0 of this instance to the VLAN 0 -# of the first instance + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,listen=:1234 +# connect the network of this instance to the network of the first instance qemu-system-i386 linux.img \ - -net nic,macaddr=52:54:00:12:34:57 \ - -net socket,connect=127.0.0.1:1234 + -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ + -netdev socket,id=n2,connect=127.0.0.1:1234 @end example @item -netdev socket,id=@var{id}[,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]] -@itemx -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]] -Create a VLAN @var{n} shared with another QEMU virtual -machines using a UDP multicast socket, effectively making a bus for -every QEMU with same multicast address @var{maddr} and @var{port}. +Configure a socket host network backend to share the guest's network traffic +with another QEMU virtual machines using a UDP multicast socket, effectively +making a bus for every QEMU with same multicast address @var{maddr} and @var{port}. NOTES: @enumerate @item @@ -2342,25 +2335,24 @@ Example: @example # launch one QEMU instance qemu-system-i386 linux.img \ - -net nic,macaddr=52:54:00:12:34:56 \ - -net socket,mcast=230.0.0.1:1234 + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,mcast=230.0.0.1:1234 # launch another QEMU instance on same "bus" qemu-system-i386 linux.img \ - -net nic,macaddr=52:54:00:12:34:57 \ - -net socket,mcast=230.0.0.1:1234 + -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ + -netdev socket,id=n2,mcast=230.0.0.1:1234 # launch yet another QEMU instance on same "bus" qemu-system-i386 linux.img \ - -net nic,macaddr=52:54:00:12:34:58 \ - -net socket,mcast=230.0.0.1:1234 + -device e1000,netdev=n3,macaddr=52:54:00:12:34:58 \ + -netdev socket,id=n3,mcast=230.0.0.1:1234 @end example Example (User Mode Linux compat.): @example -# launch QEMU instance (note mcast address selected -# is UML's default) +# launch QEMU instance (note mcast address selected is UML's default) qemu-system-i386 linux.img \ - -net nic,macaddr=52:54:00:12:34:56 \ - -net socket,mcast=239.192.168.1:1102 + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,mcast=239.192.168.1:1102 # launch UML /path/to/linux ubd0=/path/to/root_fs eth0=mcast @end example @@ -2368,14 +2360,13 @@ qemu-system-i386 linux.img \ Example (send packets from host's 1.2.3.4): @example qemu-system-i386 linux.img \ - -net nic,macaddr=52:54:00:12:34:56 \ - -net socket,mcast=239.192.168.1:1102,localaddr=1.2.3.4 + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 @end example @item -netdev l2tpv3,id=@var{id},src=@var{srcaddr},dst=@var{dstaddr}[,srcport=@var{srcport}][,dstport=@var{dstport}],txsession=@var{txsession}[,rxsession=@var{rxsession}][,ipv6][,udp][,cookie64][,counter][,pincounter][,txcookie=@var{txcookie}][,rxcookie=@var{rxcookie}][,offset=@var{offset}] -@itemx -net l2tpv3[,vlan=@var{n}][,name=@var{name}],src=@var{srcaddr},dst=@var{dstaddr}[,srcport=@var{srcport}][,dstport=@var{dstport}],txsession=@var{txsession}[,rxsession=@var{rxsession}][,ipv6][,udp][,cookie64][,counter][,pincounter][,txcookie=@var{txcookie}][,rxcookie=@var{rxcookie}][,offset=@var{offset}] -Connect VLAN @var{n} to L2TPv3 pseudowire. L2TPv3 (RFC3391) is a popular -protocol to transport Ethernet (and other Layer 2) data frames between +Configure a L2TPv3 pseudowire host network backend. L2TPv3 (RFC3391) is a +popular protocol to transport Ethernet (and other Layer 2) data frames between two systems. It is present in routers, firewalls and the Linux kernel (from version 3.3 onwards). @@ -2428,14 +2419,13 @@ brctl addif br-lan vmtunnel0 # on 4.3.2.1 # launch QEMU instance - if your network has reorder or is very lossy add ,pincounter -qemu-system-i386 linux.img -net nic -net l2tpv3,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter - +qemu-system-i386 linux.img -device e1000,netdev=n1 \ + -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter @end example @item -netdev vde,id=@var{id}[,sock=@var{socketpath}][,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}] -@itemx -net vde[,vlan=@var{n}][,name=@var{name}][,sock=@var{socketpath}] [,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}] -Connect VLAN @var{n} to PORT @var{n} of a vde switch running on host and +Configure VDE backend to connect to PORT @var{n} of a vde switch running on host and listening for incoming connections on @var{socketpath}. Use GROUP @var{groupname} and MODE @var{octalmode} to change default ownership and permissions for communication port. This option is only available if QEMU has been compiled @@ -2446,19 +2436,9 @@ Example: # launch vde switch vde_switch -F -sock /tmp/myswitch # launch QEMU instance -qemu-system-i386 linux.img -net nic -net vde,sock=/tmp/myswitch +qemu-system-i386 linux.img -nic vde,sock=/tmp/myswitch @end example -@item -netdev hubport,id=@var{id},hubid=@var{hubid}[,netdev=@var{nd}] - -Create a hub port on QEMU "vlan" @var{hubid}. - -The hubport netdev lets you connect a NIC to a QEMU "vlan" instead of a single -netdev. @code{-net} and @code{-device} with parameter @option{vlan} create the -required hub automatically. Alternatively, you can also connect the hubport -to another netdev with ID @var{nd} by using the @option{netdev=@var{nd}} -option. - @item -netdev vhost-user,chardev=@var{id}[,vhostforce=on|off][,queues=n] Establish a vhost-user netdev, backed by a chardev @var{id}. The chardev should @@ -2477,17 +2457,36 @@ qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,sha -device virtio-net-pci,netdev=net0 @end example -@item --nic [tap|bridge|user|l2tpv3|vde|netmap|vhost-user|socket][,...][,mac=macaddr] +@item -netdev hubport,id=@var{id},hubid=@var{hubid}[,netdev=@var{nd}] -This option is a shortcut for setting both, the on-board (default) guest NIC -hardware and the host network backend in one go. The host backend options are -the same as with the corresponding @option{--netdev} option. The guest NIC -hardware MAC address can be set with @option{mac=@var{macaddr}}. +Create a hub port on the emulated hub with ID @var{hubid}. -@item --nic none -Indicate that no network devices should be configured. It is used to override -the default configuration (default NIC with @option{--net user} backend) which -is activated if no other networking options are provided. +The hubport netdev lets you connect a NIC to a QEMU emulated hub instead of a +single netdev. @code{-net} and @code{-device} with the parameter @option{vlan} +(deprecated), or @code{-nic hubport} can also be used to connect a +network device or a NIC to a hub. Alternatively, you can also connect the +hubport to another netdev with ID @var{nd} by using the @option{netdev=@var{nd}} +option. + +@item -net nic[,vlan=@var{n}][,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] +@findex -net +Legacy option to configure or create an on-board (or machine default) Network +Interface Card(NIC) and connect it either to the emulated hub port ("vlan") +with number @var{n} (@var{n} = 0 is the default), or to the netdev @var{nd}. +The NIC is an e1000 by default on the PC target. Optionally, the MAC address +can be changed to @var{mac}, the device address set to @var{addr} (PCI cards +only), and a @var{name} can be assigned for use in monitor commands. +Optionally, for PCI cards, you can specify the number @var{v} of MSI-X vectors +that the card should have; this option currently only affects virtio cards; set +@var{v} = 0 to disable MSI-X. If no @option{-net} option is specified, a single +NIC is created. QEMU can emulate several different models of network card. +Use @code{-net nic,model=help} for a list of available devices for your target. + +@item -net user|tap|bridge|socket|l2tpv3|vde[,...][,vlan=@var{n}][,name=@var{name}] +Configure a host network backend (with the options corresponding to the same +@option{-netdev} option) and connect it to the emulated hub ("vlan") with the +number @var{n} (default is number 0). Use @var{name} to specify the name of the +hub port. ETEXI STEXI diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 2c05e3c284..3e14bc41f2 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -872,7 +872,7 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPISemError(info, "'%s' of %s '%s' should only use false value" % (key, meta, name)) - if key == 'boxed' and value is not True: + if (key == 'boxed' or key == 'allow-oob') and value is not True: raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 15870a4f36..c36753954b 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -86,25 +86,25 @@ static void assert_hvf_ok(hv_return_t ret) switch (ret) { case HV_ERROR: - error_report("Error: HV_ERROR\n"); + error_report("Error: HV_ERROR"); break; case HV_BUSY: - error_report("Error: HV_BUSY\n"); + error_report("Error: HV_BUSY"); break; case HV_BAD_ARGUMENT: - error_report("Error: HV_BAD_ARGUMENT\n"); + error_report("Error: HV_BAD_ARGUMENT"); break; case HV_NO_RESOURCES: - error_report("Error: HV_NO_RESOURCES\n"); + error_report("Error: HV_NO_RESOURCES"); break; case HV_NO_DEVICE: - error_report("Error: HV_NO_DEVICE\n"); + error_report("Error: HV_NO_DEVICE"); break; case HV_UNSUPPORTED: - error_report("Error: HV_UNSUPPORTED\n"); + error_report("Error: HV_UNSUPPORTED"); break; default: - error_report("Unknown Error\n"); + error_report("Unknown Error"); } abort(); @@ -191,7 +191,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add) if (mem) { mem->size = 0; if (do_hvf_set_memory(mem)) { - error_report("Failed to reset overlapping slot\n"); + error_report("Failed to reset overlapping slot"); abort(); } } @@ -211,7 +211,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add) } if (x == hvf_state->num_slots) { - error_report("No free slots\n"); + error_report("No free slots"); abort(); } @@ -221,7 +221,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add) mem->region = area; if (do_hvf_set_memory(mem)) { - error_report("Error registering new memory slot\n"); + error_report("Error registering new memory slot"); abort(); } } @@ -884,7 +884,7 @@ int hvf_vcpu_exec(CPUState *cpu) break; } default: - error_report("Unrecognized CR %d\n", cr); + error_report("Unrecognized CR %d", cr); abort(); } RIP(env) += ins_len; @@ -930,7 +930,7 @@ int hvf_vcpu_exec(CPUState *cpu) env->error_code = 0; break; default: - error_report("%llx: unhandled exit %llx\n", rip, exit_reason); + error_report("%llx: unhandled exit %llx", rip, exit_reason); } } while (ret == 0); diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9de34d7099..5a527fbba0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -115,6 +115,8 @@ static void riscv_any_cpu_init(Object *obj) set_resetvec(env, DEFAULT_RSTVEC); } +#if defined(TARGET_RISCV32) + static void rv32gcsu_priv1_09_1_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; @@ -141,6 +143,8 @@ static void rv32imacu_nommu_cpu_init(Object *obj) set_resetvec(env, DEFAULT_RSTVEC); } +#elif defined(TARGET_RISCV64) + static void rv64gcsu_priv1_09_1_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; @@ -167,20 +171,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj) set_resetvec(env, DEFAULT_RSTVEC); } -static const RISCVCPUInfo riscv_cpus[] = { - { 96, TYPE_RISCV_CPU_ANY, riscv_any_cpu_init }, - { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init }, - { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init }, - { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init }, - { 32, TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init }, - { 32, TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init }, - { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init }, - { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init }, - { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init }, - { 64, TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init }, - { 64, TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init }, - { 0, NULL, NULL } -}; +#endif static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) { @@ -366,28 +357,6 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) cc->vmsd = &vmstate_riscv_cpu; } -static void cpu_register(const RISCVCPUInfo *info) -{ - TypeInfo type_info = { - .name = info->name, - .parent = TYPE_RISCV_CPU, - .instance_size = sizeof(RISCVCPU), - .instance_init = info->initfn, - }; - - type_register(&type_info); -} - -static const TypeInfo riscv_cpu_type_info = { - .name = TYPE_RISCV_CPU, - .parent = TYPE_CPU, - .instance_size = sizeof(RISCVCPU), - .instance_init = riscv_cpu_init, - .abstract = false, - .class_size = sizeof(RISCVCPUClass), - .class_init = riscv_cpu_class_init, -}; - char *riscv_isa_string(RISCVCPU *cpu) { int i; @@ -403,30 +372,76 @@ char *riscv_isa_string(RISCVCPU *cpu) return isa_str; } -void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf) +typedef struct RISCVCPUListState { + fprintf_function cpu_fprintf; + FILE *file; +} RISCVCPUListState; + +static gint riscv_cpu_list_compare(gconstpointer a, gconstpointer b) { - const RISCVCPUInfo *info = riscv_cpus; + ObjectClass *class_a = (ObjectClass *)a; + ObjectClass *class_b = (ObjectClass *)b; + const char *name_a, *name_b; - while (info->name) { - if (info->bit_widths & TARGET_LONG_BITS) { - (*cpu_fprintf)(f, "%s\n", info->name); - } - info++; - } + name_a = object_class_get_name(class_a); + name_b = object_class_get_name(class_b); + return strcmp(name_a, name_b); } -static void riscv_cpu_register_types(void) +static void riscv_cpu_list_entry(gpointer data, gpointer user_data) { - const RISCVCPUInfo *info = riscv_cpus; + RISCVCPUListState *s = user_data; + const char *typename = object_class_get_name(OBJECT_CLASS(data)); + int len = strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX); - type_register_static(&riscv_cpu_type_info); + (*s->cpu_fprintf)(s->file, "%.*s\n", len, typename); +} - while (info->name) { - if (info->bit_widths & TARGET_LONG_BITS) { - cpu_register(info); - } - info++; - } +void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + RISCVCPUListState s = { + .cpu_fprintf = cpu_fprintf, + .file = f, + }; + GSList *list; + + list = object_class_get_list(TYPE_RISCV_CPU, false); + list = g_slist_sort(list, riscv_cpu_list_compare); + g_slist_foreach(list, riscv_cpu_list_entry, &s); + g_slist_free(list); } -type_init(riscv_cpu_register_types) +#define DEFINE_CPU(type_name, initfn) \ + { \ + .name = type_name, \ + .parent = TYPE_RISCV_CPU, \ + .instance_init = initfn \ + } + +static const TypeInfo riscv_cpu_type_infos[] = { + { + .name = TYPE_RISCV_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(RISCVCPU), + .instance_init = riscv_cpu_init, + .abstract = true, + .class_size = sizeof(RISCVCPUClass), + .class_init = riscv_cpu_class_init, + }, + DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init), +#if defined(TARGET_RISCV32) + DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init) +#elif defined(TARGET_RISCV64) + DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init) +#endif +}; + +DEFINE_TYPES(riscv_cpu_type_infos) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index e34715df4e..7c6068bac9 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -144,8 +144,21 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, } mstatus = (mstatus & ~mask) | (val_to_write & mask); - int dirty = (mstatus & MSTATUS_FS) == MSTATUS_FS; - dirty |= (mstatus & MSTATUS_XS) == MSTATUS_XS; + + /* Note: this is a workaround for an issue where mstatus.FS + does not report dirty after floating point operations + that modify floating point state. This workaround is + technically compliant with the RISC-V Privileged + specification as it is legal to return only off, or dirty. + at the expense of extra floating point save/restore. */ + + /* FP is always dirty or off */ + if (mstatus & MSTATUS_FS) { + mstatus |= MSTATUS_FS; + } + + int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | + ((mstatus & MSTATUS_XS) == MSTATUS_XS); mstatus = set_field(mstatus, MSTATUS_SD, dirty); env->mstatus = mstatus; break; diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h index d81a6c4535..e3a43aabb6 100644 --- a/tcg/tcg-opc.h +++ b/tcg/tcg-opc.h @@ -182,8 +182,8 @@ DEF(add2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_add2_i64)) DEF(sub2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_sub2_i64)) DEF(mulu2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulu2_i64)) DEF(muls2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muls2_i64)) -DEF(muluh_i64, 1, 2, 0, IMPL(TCG_TARGET_HAS_muluh_i64)) -DEF(mulsh_i64, 1, 2, 0, IMPL(TCG_TARGET_HAS_mulsh_i64)) +DEF(muluh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muluh_i64)) +DEF(mulsh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulsh_i64)) #define TLADDR_ARGS (TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? 1 : 2) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) diff --git a/tests/Makefile.include b/tests/Makefile.include index eb218a9539..3b9a5e31a2 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -523,6 +523,7 @@ qapi-schema += missing-comma-object.json qapi-schema += missing-type.json qapi-schema += nested-struct-data.json qapi-schema += non-objects.json +qapi-schema += oob-test.json qapi-schema += pragma-doc-required-crap.json qapi-schema += pragma-extra-junk.json qapi-schema += pragma-name-case-whitelist-crap.json diff --git a/tests/libqtest.c b/tests/libqtest.c index 200b2b9e92..6f33a37667 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -166,7 +166,8 @@ static const char *qtest_qemu_binary(void) return qemu_bin; } -QTestState *qtest_init_without_qmp_handshake(const char *extra_args) +QTestState *qtest_init_without_qmp_handshake(bool use_oob, + const char *extra_args) { QTestState *s; int sock, qmpsock, i; @@ -199,12 +200,13 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) command = g_strdup_printf("exec %s " "-qtest unix:%s,nowait " "-qtest-log %s " - "-qmp unix:%s,nowait " + "-chardev socket,path=%s,nowait,id=char0 " + "-mon chardev=char0,mode=control%s " "-machine accel=qtest " "-display none " "%s", qemu_binary, socket_path, getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", - qmp_socket_path, + qmp_socket_path, use_oob ? ",x-oob=on" : "", extra_args ?: ""); execlp("/bin/sh", "sh", "-c", command, NULL); exit(1); @@ -239,7 +241,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) QTestState *qtest_init(const char *extra_args) { - QTestState *s = qtest_init_without_qmp_handshake(extra_args); + QTestState *s = qtest_init_without_qmp_handshake(false, extra_args); /* Read the QMP greeting and then do the handshake */ qtest_qmp_discard_response(s, ""); diff --git a/tests/libqtest.h b/tests/libqtest.h index 811169453a..cbe8df4473 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -56,11 +56,14 @@ QTestState *qtest_init(const char *extra_args); /** * qtest_init_without_qmp_handshake: - * @extra_args: other arguments to pass to QEMU. + * @use_oob: true to have the server advertise OOB support + * @extra_args: other arguments to pass to QEMU. CAUTION: these + * arguments are subject to word splitting and shell evaluation. * * Returns: #QTestState instance. */ -QTestState *qtest_init_without_qmp_handshake(const char *extra_args); +QTestState *qtest_init_without_qmp_handshake(bool use_oob, + const char *extra_args); /** * qtest_quit: diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 430b5a87db..63058b1590 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -28,9 +28,9 @@ object q_obj_cmd-arg member arg2: str optional=True member arg3: bool optional=False command cmd q_obj_cmd-arg -> Object - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False command cmd-boxed Object -> None - gen=True success_response=True boxed=True + gen=True success_response=True boxed=True oob=False doc freeform body= = Section diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index ee3b34e623..82213aa51d 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -5,4 +5,4 @@ module ident-with-escape.json object q_obj_fooA-arg member bar1: str optional=False command fooA q_obj_fooA-arg -> None - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index a79935e8c3..862678f8f4 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -3,6 +3,6 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE module indented-expr.json command eins None -> None - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False command zwei None -> None - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False diff --git a/tests/qapi-schema/oob-test.err b/tests/qapi-schema/oob-test.err new file mode 100644 index 0000000000..35b60f7480 --- /dev/null +++ b/tests/qapi-schema/oob-test.err @@ -0,0 +1 @@ +tests/qapi-schema/oob-test.json:2: 'allow-oob' of command 'oob-command-1' should only use true value diff --git a/tests/qapi-schema/oob-test.exit b/tests/qapi-schema/oob-test.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/oob-test.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/oob-test.json b/tests/qapi-schema/oob-test.json new file mode 100644 index 0000000000..da9635920f --- /dev/null +++ b/tests/qapi-schema/oob-test.json @@ -0,0 +1,2 @@ +# Check against oob illegal value +{ 'command': 'oob-command-1', 'allow-oob': 'some-string' } diff --git a/tests/qapi-schema/oob-test.out b/tests/qapi-schema/oob-test.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/oob-test.out diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index c72dbd8050..06e30f452e 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -139,6 +139,9 @@ { 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' } { 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true } +# Smoke test on Out-Of-Band +{ 'command': 'an-oob-command', 'allow-oob': true } + # For testing integer range flattening in opts-visitor. The following schema # corresponds to the option format: # diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 012e7fc06a..467577d770 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -16,7 +16,7 @@ object Empty1 object Empty2 base Empty1 command user_def_cmd0 Empty2 -> Empty2 - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False enum QEnumTwo ['value1', 'value2'] prefix QENUM_TWO object UserDefOne @@ -143,29 +143,31 @@ object UserDefNativeListUnion case sizes: q_obj_sizeList-wrapper case any: q_obj_anyList-wrapper command user_def_cmd None -> None - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False object q_obj_user_def_cmd1-arg member ud1a: UserDefOne optional=False command user_def_cmd1 q_obj_user_def_cmd1-arg -> None - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False object q_obj_user_def_cmd2-arg member ud1a: UserDefOne optional=False member ud1b: UserDefOne optional=True command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False object q_obj_guest-get-time-arg member a: int optional=False member b: int optional=True command guest-get-time q_obj_guest-get-time-arg -> int - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False object q_obj_guest-sync-arg member arg: any optional=False command guest-sync q_obj_guest-sync-arg -> any - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False command boxed-struct UserDefZero -> None - gen=True success_response=True boxed=True + gen=True success_response=True boxed=True oob=False command boxed-union UserDefNativeListUnion -> None - gen=True success_response=True boxed=True + gen=True success_response=True boxed=True oob=False +command an-oob-command None -> None + gen=True success_response=True boxed=False oob=True object UserDefOptions member i64: intList optional=True member u64: uint64List optional=True @@ -229,4 +231,4 @@ object q_obj___org.qemu_x-command-arg member c: __org.qemu_x-Union2 optional=False member d: __org.qemu_x-Alt optional=False command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 - gen=True success_response=True boxed=False + gen=True success_response=True boxed=False oob=False diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 10e68b01d9..c1a144ba29 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -45,8 +45,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): gen, success_response, boxed, allow_oob): print('command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name)) - print(' gen=%s success_response=%s boxed=%s' % \ - (gen, success_response, boxed)) + print(' gen=%s success_response=%s boxed=%s oob=%s' % \ + (gen, success_response, boxed, allow_oob)) def visit_event(self, name, info, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 558e83540c..772058fc4c 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -81,7 +81,7 @@ static void test_qmp_protocol(void) QList *capabilities; QTestState *qts; - qts = qtest_init_without_qmp_handshake(common_args); + qts = qtest_init_without_qmp_handshake(false, common_args); /* Test greeting */ resp = qtest_qmp_receive(qts); @@ -90,6 +90,7 @@ static void test_qmp_protocol(void) test_version(qdict_get(q, "version")); capabilities = qdict_get_qlist(q, "capabilities"); g_assert(capabilities && qlist_empty(capabilities)); + QDECREF(resp); /* Test valid command before handshake */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); @@ -134,6 +135,87 @@ static void test_qmp_protocol(void) qtest_quit(qts); } +/* Tests for Out-Of-Band support. */ +static void test_qmp_oob(void) +{ + QTestState *qts; + QDict *resp, *q; + int acks = 0; + const QListEntry *entry; + QList *capabilities; + QString *qstr; + const char *cmd_id; + + qts = qtest_init_without_qmp_handshake(true, common_args); + + /* Check the greeting message. */ + resp = qtest_qmp_receive(qts); + q = qdict_get_qdict(resp, "QMP"); + g_assert(q); + capabilities = qdict_get_qlist(q, "capabilities"); + g_assert(capabilities && !qlist_empty(capabilities)); + entry = qlist_first(capabilities); + g_assert(entry); + qstr = qobject_to(QString, entry->value); + g_assert(qstr); + g_assert_cmpstr(qstring_get_str(qstr), ==, "oob"); + QDECREF(resp); + + /* Try a fake capability, it should fail. */ + resp = qtest_qmp(qts, + "{ 'execute': 'qmp_capabilities', " + " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }"); + g_assert(qdict_haskey(resp, "error")); + QDECREF(resp); + + /* Now, enable OOB in current QMP session, it should succeed. */ + resp = qtest_qmp(qts, + "{ 'execute': 'qmp_capabilities', " + " 'arguments': { 'enable': [ 'oob' ] } }"); + g_assert(qdict_haskey(resp, "return")); + QDECREF(resp); + + /* + * Try any command that does not support OOB but with OOB flag. We + * should get failure. + */ + resp = qtest_qmp(qts, + "{ 'execute': 'query-cpus'," + " 'control': { 'run-oob': true } }"); + g_assert(qdict_haskey(resp, "error")); + QDECREF(resp); + + /* + * First send the "x-oob-test" command with lock=true and + * oob=false, it should hang the dispatcher and main thread; + * later, we send another lock=false with oob=true to continue + * that thread processing. Finally we should receive replies from + * both commands. + */ + qtest_async_qmp(qts, + "{ 'execute': 'x-oob-test'," + " 'arguments': { 'lock': true }, " + " 'id': 'lock-cmd'}"); + qtest_async_qmp(qts, + "{ 'execute': 'x-oob-test', " + " 'arguments': { 'lock': false }, " + " 'control': { 'run-oob': true }, " + " 'id': 'unlock-cmd' }"); + + /* Ignore all events. Wait for 2 acks */ + while (acks < 2) { + resp = qtest_qmp_receive(qts); + cmd_id = qdict_get_str(resp, "id"); + if (!g_strcmp0(cmd_id, "lock-cmd") || + !g_strcmp0(cmd_id, "unlock-cmd")) { + acks++; + } + QDECREF(resp); + } + + qtest_quit(qts); +} + static int query_error_class(const char *cmd) { static struct { @@ -318,6 +400,7 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); qtest_add_func("qmp/protocol", test_qmp_protocol); + qtest_add_func("qmp/oob", test_qmp_oob); qmp_schema_init(&schema); add_query_tests(&schema); diff --git a/tests/test-aio.c b/tests/test-aio.c index 54e20d6ab1..86fb73b3d5 100644 --- a/tests/test-aio.c +++ b/tests/test-aio.c @@ -16,6 +16,8 @@ #include "qemu/timer.h" #include "qemu/sockets.h" #include "qemu/error-report.h" +#include "qemu/coroutine.h" +#include "qemu/main-loop.h" static AioContext *ctx; @@ -827,24 +829,59 @@ static void test_source_timer_schedule(void) timer_del(&data.timer); } +/* + * Check that aio_co_enter() can chain many times + * + * Two coroutines should be able to invoke each other via aio_co_enter() many + * times without hitting a limit like stack exhaustion. In other words, the + * calls should be chained instead of nested. + */ -/* End of tests. */ +typedef struct { + Coroutine *other; + unsigned i; + unsigned max; +} ChainData; -int main(int argc, char **argv) +static void coroutine_fn chain(void *opaque) { - Error *local_error = NULL; - GSource *src; + ChainData *data = opaque; - init_clocks(NULL); + for (data->i = 0; data->i < data->max; data->i++) { + /* Queue up the other coroutine... */ + aio_co_enter(ctx, data->other); - ctx = aio_context_new(&local_error); - if (!ctx) { - error_reportf_err(local_error, "Failed to create AIO Context: "); - exit(1); + /* ...and give control to it */ + qemu_coroutine_yield(); } - src = aio_get_g_source(ctx); - g_source_attach(src, NULL); - g_source_unref(src); +} + +static void test_queue_chaining(void) +{ + /* This number of iterations hit stack exhaustion in the past: */ + ChainData data_a = { .max = 25000 }; + ChainData data_b = { .max = 25000 }; + + data_b.other = qemu_coroutine_create(chain, &data_a); + data_a.other = qemu_coroutine_create(chain, &data_b); + + qemu_coroutine_enter(data_b.other); + + g_assert_cmpint(data_a.i, ==, data_a.max); + g_assert_cmpint(data_b.i, ==, data_b.max - 1); + + /* Allow the second coroutine to terminate */ + qemu_coroutine_enter(data_a.other); + + g_assert_cmpint(data_b.i, ==, data_b.max); +} + +/* End of tests. */ + +int main(int argc, char **argv) +{ + qemu_init_main_loop(&error_fatal); + ctx = qemu_get_aio_context(); while (g_main_context_iteration(NULL, false)); @@ -864,6 +901,8 @@ int main(int argc, char **argv) g_test_add_func("/aio/external-client", test_aio_external_client); g_test_add_func("/aio/timer/schedule", test_timer_schedule); + g_test_add_func("/aio/coroutine/queue-chaining", test_queue_chaining); + g_test_add_func("/aio-gsource/flush", test_source_flush); g_test_add_func("/aio-gsource/bh/schedule", test_source_bh_schedule); g_test_add_func("/aio-gsource/bh/schedule10", test_source_bh_schedule10); diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 93fbbb1b73..db690cc5ae 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -16,6 +16,10 @@ void qmp_user_def_cmd(Error **errp) { } +void qmp_an_oob_command(Error **errp) +{ +} + Empty2 *qmp_user_def_cmd0(Error **errp) { return g_new0(Empty2, 1); diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index 438c18a0d6..d18d90db2c 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -1115,7 +1115,7 @@ static const SerializeOps visitors[] = { static void add_visitor_type(const SerializeOps *ops) { - char testname_prefix[128]; + char testname_prefix[32]; char testname[128]; TestArgs *args; int i = 0; diff --git a/tests/tpm-crb-test.c b/tests/tpm-crb-test.c index e1513cb69e..d8f9569203 100644 --- a/tests/tpm-crb-test.c +++ b/tests/tpm-crb-test.c @@ -28,6 +28,10 @@ static void tpm_crb_test(const void *data) uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE); uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); + uint8_t locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); + uint32_t locctrl = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL); + uint32_t locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); + uint32_t sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1); g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1); @@ -45,9 +49,47 @@ static void tpm_crb_test(const void *data) g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE); g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); + + g_assert_cmpint(locctrl, ==, 0); + + g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 0); + g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0); + + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); + + /* request access to locality 0 */ + writeb(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); + + /* granted bit must be set now */ + locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); + g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 1); + g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0); + + /* we must have an assigned locality */ + locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 1); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); + + /* set into ready state */ + writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 1); + + /* TPM must not be in the idle state */ + sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); + memwrite(caddr, TPM_CMD, sizeof(TPM_CMD)); - uint32_t sts, start = 1; + uint32_t start = 1; uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); do { @@ -58,12 +100,40 @@ static void tpm_crb_test(const void *data) } while (g_get_monotonic_time() < end_time); start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); g_assert_cmpint(start & 1, ==, 0); + + /* TPM must still not be in the idle state */ sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); - g_assert_cmpint(sts & 1, ==, 0); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); struct tpm_hdr tpm_msg; memread(raddr, &tpm_msg, sizeof(tpm_msg)); g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); + + /* set TPM into idle state */ + writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 2); + + /* idle state must be indicated now */ + sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1); + g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); + + /* relinquish locality */ + writel(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 2); + + /* Granted flag must be cleared */ + sts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); + g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, Granted), ==, 0); + g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, beenSeized), ==, 0); + + /* no locality may be assigned */ + locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); + g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); + } int main(int argc, char **argv) diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c index 5a80c10690..27438a1858 100644 --- a/util/qemu-coroutine-lock.c +++ b/util/qemu-coroutine-lock.c @@ -68,40 +68,6 @@ void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock) } } -/** - * qemu_co_queue_run_restart: - * - * Enter each coroutine that was previously marked for restart by - * qemu_co_queue_next() or qemu_co_queue_restart_all(). This function is - * invoked by the core coroutine code when the current coroutine yields or - * terminates. - */ -void qemu_co_queue_run_restart(Coroutine *co) -{ - Coroutine *next; - QSIMPLEQ_HEAD(, Coroutine) tmp_queue_wakeup = - QSIMPLEQ_HEAD_INITIALIZER(tmp_queue_wakeup); - - trace_qemu_co_queue_run_restart(co); - - /* Because "co" has yielded, any coroutine that we wakeup can resume it. - * If this happens and "co" terminates, co->co_queue_wakeup becomes - * invalid memory. Therefore, use a temporary queue and do not touch - * the "co" coroutine as soon as you enter another one. - * - * In its turn resumed "co" can populate "co_queue_wakeup" queue with - * new coroutines to be woken up. The caller, who has resumed "co", - * will be responsible for traversing the same queue, which may cause - * a different wakeup order but not any missing wakeups. - */ - QSIMPLEQ_CONCAT(&tmp_queue_wakeup, &co->co_queue_wakeup); - - while ((next = QSIMPLEQ_FIRST(&tmp_queue_wakeup))) { - QSIMPLEQ_REMOVE_HEAD(&tmp_queue_wakeup, co_queue_next); - qemu_coroutine_enter(next); - } -} - static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) { Coroutine *next; diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 9eff7fd450..1ba4191b84 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -104,57 +104,65 @@ static void coroutine_delete(Coroutine *co) void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) { - Coroutine *self = qemu_coroutine_self(); - CoroutineAction ret; - - /* Cannot rely on the read barrier for co in aio_co_wake(), as there are - * callers outside of aio_co_wake() */ - const char *scheduled = atomic_mb_read(&co->scheduled); + QSIMPLEQ_HEAD(, Coroutine) pending = QSIMPLEQ_HEAD_INITIALIZER(pending); + Coroutine *from = qemu_coroutine_self(); - trace_qemu_aio_coroutine_enter(ctx, self, co, co->entry_arg); + QSIMPLEQ_INSERT_TAIL(&pending, co, co_queue_next); - /* if the Coroutine has already been scheduled, entering it again will - * cause us to enter it twice, potentially even after the coroutine has - * been deleted */ - if (scheduled) { - fprintf(stderr, - "%s: Co-routine was already scheduled in '%s'\n", - __func__, scheduled); - abort(); - } + /* Run co and any queued coroutines */ + while (!QSIMPLEQ_EMPTY(&pending)) { + Coroutine *to = QSIMPLEQ_FIRST(&pending); + CoroutineAction ret; - if (co->caller) { - fprintf(stderr, "Co-routine re-entered recursively\n"); - abort(); - } + /* Cannot rely on the read barrier for to in aio_co_wake(), as there are + * callers outside of aio_co_wake() */ + const char *scheduled = atomic_mb_read(&to->scheduled); - co->caller = self; - co->ctx = ctx; + QSIMPLEQ_REMOVE_HEAD(&pending, co_queue_next); - /* Store co->ctx before anything that stores co. Matches - * barrier in aio_co_wake and qemu_co_mutex_wake. - */ - smp_wmb(); + trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg); - ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER); - - qemu_co_queue_run_restart(co); + /* if the Coroutine has already been scheduled, entering it again will + * cause us to enter it twice, potentially even after the coroutine has + * been deleted */ + if (scheduled) { + fprintf(stderr, + "%s: Co-routine was already scheduled in '%s'\n", + __func__, scheduled); + abort(); + } - /* Beware, if ret == COROUTINE_YIELD and qemu_co_queue_run_restart() - * has started any other coroutine, "co" might have been reentered - * and even freed by now! So be careful and do not touch it. - */ + if (to->caller) { + fprintf(stderr, "Co-routine re-entered recursively\n"); + abort(); + } - switch (ret) { - case COROUTINE_YIELD: - return; - case COROUTINE_TERMINATE: - assert(!co->locks_held); - trace_qemu_coroutine_terminate(co); - coroutine_delete(co); - return; - default: - abort(); + to->caller = from; + to->ctx = ctx; + + /* Store to->ctx before anything that stores to. Matches + * barrier in aio_co_wake and qemu_co_mutex_wake. + */ + smp_wmb(); + + ret = qemu_coroutine_switch(from, to, COROUTINE_ENTER); + + /* Queued coroutines are run depth-first; previously pending coroutines + * run after those queued more recently. + */ + QSIMPLEQ_PREPEND(&pending, &to->co_queue_wakeup); + + switch (ret) { + case COROUTINE_YIELD: + break; + case COROUTINE_TERMINATE: + assert(!to->locks_held); + trace_qemu_coroutine_terminate(to); + coroutine_delete(to); + break; + default: + abort(); + } } } @@ -2404,6 +2404,11 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) if (qemu_opt_get_bool(opts, "pretty", 0)) flags |= MONITOR_USE_PRETTY; + /* OOB is off by default */ + if (qemu_opt_get_bool(opts, "x-oob", 0)) { + flags |= MONITOR_USE_OOB; + } + chardev = qemu_opt_get(opts, "chardev"); chr = qemu_chr_find(chardev); if (chr == NULL) { @@ -4728,6 +4733,8 @@ int main(int argc, char **argv, char **envp) main_loop(); + gdbserver_cleanup(); + /* No more vcpu or device emulation activity beyond this point */ vm_shutdown(); |