aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--qapi/qom.json17
-rw-r--r--softmmu/qtest.c185
-rw-r--r--softmmu/vl.c5
3 files changed, 196 insertions, 11 deletions
diff --git a/qapi/qom.json b/qapi/qom.json
index 4f48035..f7ef30f 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -645,6 +645,21 @@
'data': { 'path': 'str' } }
##
+# @QtestProperties:
+#
+# Properties for qtest objects.
+#
+# @chardev: the chardev to be used to receive qtest commands on.
+#
+# @log: the path to a log file
+#
+# Since: 6.0
+##
+{ 'struct': 'QtestProperties',
+ 'data': { 'chardev': 'str',
+ '*log': 'str' } }
+
+##
# @RemoteObjectProperties:
#
# Properties for x-remote-object objects.
@@ -769,6 +784,7 @@
'memory-backend-ram',
'pef-guest',
'pr-manager-helper',
+ 'qtest',
'rng-builtin',
'rng-egd',
'rng-random',
@@ -825,6 +841,7 @@
'if': 'defined(CONFIG_LINUX)' },
'memory-backend-ram': 'MemoryBackendProperties',
'pr-manager-helper': 'PrManagerHelperProperties',
+ 'qtest': 'QtestProperties',
'rng-builtin': 'RngProperties',
'rng-egd': 'RngEgdProperties',
'rng-random': 'RngRandomProperties',
diff --git a/softmmu/qtest.c b/softmmu/qtest.c
index 130c366..72751e1 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);
diff --git a/softmmu/vl.c b/softmmu/vl.c
index 93e7846..11ac375 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -1758,8 +1758,9 @@ static bool object_create_early(const char *type)
* add one, state the reason in a comment!
*/
- /* Reason: rng-egd property "chardev" */
- if (g_str_equal(type, "rng-egd")) {
+ /* Reason: property "chardev" */
+ if (g_str_equal(type, "rng-egd") ||
+ g_str_equal(type, "qtest")) {
return false;
}