diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2020-11-09 10:13:30 -0500 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2021-05-26 14:49:45 +0200 |
commit | 6ba7ada3559ed464c06cea7efa4c66f8f2ccbf5b (patch) | |
tree | 41d9948ce3a8aae2a23bbcc6955eb21f9dc9df93 /softmmu/qtest.c | |
parent | 9e33013bd494b43c81a2730b9f5cba2b5743343b (diff) | |
download | qemu-6ba7ada3559ed464c06cea7efa4c66f8f2ccbf5b.zip |
qtest: add a QOM object for qtest
The qtest server right now can only be created using the -qtest
and -qtest-log options. Allow an alternative way to create it
using "-object qtest,chardev=...,log=...".
This is part of the long term plan to make more (or all) of
QEMU configurable through QMP and preconfig mode.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'softmmu/qtest.c')
-rw-r--r-- | softmmu/qtest.c | 185 |
1 files changed, 176 insertions, 9 deletions
diff --git a/softmmu/qtest.c b/softmmu/qtest.c index 130c366615..72751e1fd8 100644 --- a/softmmu/qtest.c +++ b/softmmu/qtest.c @@ -27,6 +27,8 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/cutils.h" +#include "qapi/qmp/qerror.h" +#include "qom/object_interfaces.h" #include CONFIG_DEVICES #ifdef CONFIG_PSERIES #include "hw/ppc/spapr_rtas.h" @@ -34,11 +36,25 @@ #define MAX_IRQ 256 +#define TYPE_QTEST "qtest" + +OBJECT_DECLARE_SIMPLE_TYPE(QTest, QTEST) + +struct QTest { + Object parent; + + bool has_machine_link; + char *chr_name; + Chardev *chr; + CharBackend qtest_chr; + char *log; +}; + bool qtest_allowed; static DeviceState *irq_intercept_dev; static FILE *qtest_log_fp; -static CharBackend qtest_chr; +static QTest *qtest; static GString *inbuf; static int irq_levels[MAX_IRQ]; static qemu_timeval start_time; @@ -320,7 +336,7 @@ static void qtest_irq_handler(void *opaque, int n, int level) qemu_set_irq(old_irq, level); if (irq_levels[n] != level) { - CharBackend *chr = &qtest_chr; + CharBackend *chr = &qtest->qtest_chr; irq_levels[n] = level; qtest_send_prefix(chr); qtest_sendf(chr, "IRQ %s %d\n", @@ -849,18 +865,39 @@ static void qtest_event(void *opaque, QEMUChrEvent event) break; } } + void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) { + ERRP_GUARD(); Chardev *chr; + Object *qtest; chr = qemu_chr_new("qtest", qtest_chrdev, NULL); - if (chr == NULL) { error_setg(errp, "Failed to initialize device for qtest: \"%s\"", qtest_chrdev); return; } + qtest = object_new(TYPE_QTEST); + object_property_set_str(qtest, "chardev", "qtest", &error_abort); + if (qtest_log) { + object_property_set_str(qtest, "log", qtest_log, &error_abort); + } + object_property_add_child(qdev_get_machine(), "qtest", qtest); + user_creatable_complete(USER_CREATABLE(qtest), errp); + if (*errp) { + object_unparent(qtest); + } + object_unref(OBJECT(chr)); + object_unref(qtest); +} + +static bool qtest_server_start(QTest *q, Error **errp) +{ + Chardev *chr = q->chr; + const char *qtest_log = q->log; + if (qtest_log) { if (strcmp(qtest_log, "none") != 0) { qtest_log_fp = fopen(qtest_log, "w+"); @@ -869,16 +906,20 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** qtest_log_fp = stderr; } - qemu_chr_fe_init(&qtest_chr, chr, errp); - qemu_chr_fe_set_handlers(&qtest_chr, qtest_can_read, qtest_read, - qtest_event, NULL, &qtest_chr, NULL, true); - qemu_chr_fe_set_echo(&qtest_chr, true); + if (!qemu_chr_fe_init(&q->qtest_chr, chr, errp)) { + return false; + } + qemu_chr_fe_set_handlers(&q->qtest_chr, qtest_can_read, qtest_read, + qtest_event, NULL, &q->qtest_chr, NULL, true); + qemu_chr_fe_set_echo(&q->qtest_chr, true); inbuf = g_string_new(""); if (!qtest_server_send) { - qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr); + qtest_server_set_send_handler(qtest_server_char_be_send, &q->qtest_chr); } + qtest = q; + return true; } void qtest_server_set_send_handler(void (*send)(void*, const char*), @@ -890,7 +931,7 @@ void qtest_server_set_send_handler(void (*send)(void*, const char*), bool qtest_driver(void) { - return qtest_chr.chr != NULL; + return qtest && qtest->qtest_chr.chr != NULL; } void qtest_server_inproc_recv(void *dummy, const char *buf) @@ -905,3 +946,129 @@ void qtest_server_inproc_recv(void *dummy, const char *buf) g_string_truncate(gstr, 0); } } + +static void qtest_complete(UserCreatable *uc, Error **errp) +{ + QTest *q = QTEST(uc); + if (qtest) { + error_setg(errp, "Only one instance of qtest can be created"); + return; + } + if (!q->chr_name) { + error_setg(errp, "No backend specified"); + return; + } + + if (OBJECT(uc)->parent != qdev_get_machine()) { + q->has_machine_link = true; + object_property_add_const_link(qdev_get_machine(), "qtest", OBJECT(uc)); + } else { + /* -qtest was used. */ + } + + qtest_server_start(q, errp); +} + +static void qtest_unparent(Object *obj) +{ + QTest *q = QTEST(obj); + + if (qtest == q) { + qemu_chr_fe_disconnect(&q->qtest_chr); + assert(!qtest_opened); + qemu_chr_fe_deinit(&q->qtest_chr, false); + if (qtest_log_fp) { + fclose(qtest_log_fp); + qtest_log_fp = NULL; + } + qtest = NULL; + } + + if (q->has_machine_link) { + object_property_del(qdev_get_machine(), "qtest"); + q->has_machine_link = false; + } +} + +static void qtest_set_log(Object *obj, const char *value, Error **errp) +{ + QTest *q = QTEST(obj); + + if (qtest == q) { + error_setg(errp, QERR_PERMISSION_DENIED); + } else { + g_free(q->log); + q->log = g_strdup(value); + } +} + +static char *qtest_get_log(Object *obj, Error **errp) +{ + QTest *q = QTEST(obj); + + return g_strdup(q->log); +} + +static void qtest_set_chardev(Object *obj, const char *value, Error **errp) +{ + QTest *q = QTEST(obj); + Chardev *chr; + + if (qtest == q) { + error_setg(errp, QERR_PERMISSION_DENIED); + return; + } + + chr = qemu_chr_find(value); + if (!chr) { + error_setg(errp, "Cannot find character device '%s'", value); + return; + } + + g_free(q->chr_name); + q->chr_name = g_strdup(value); + + if (q->chr) { + object_unref(q->chr); + } + q->chr = chr; + object_ref(chr); +} + +static char *qtest_get_chardev(Object *obj, Error **errp) +{ + QTest *q = QTEST(obj); + + return g_strdup(q->chr_name); +} + +static void qtest_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + oc->unparent = qtest_unparent; + ucc->complete = qtest_complete; + + object_class_property_add_str(oc, "chardev", + qtest_get_chardev, qtest_set_chardev); + object_class_property_add_str(oc, "log", + qtest_get_log, qtest_set_log); +} + +static const TypeInfo qtest_info = { + .name = TYPE_QTEST, + .parent = TYPE_OBJECT, + .class_init = qtest_class_init, + .instance_size = sizeof(QTest), + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&qtest_info); +} + +type_init(register_types); |