aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2017-04-26 15:32:20 +0100
committerPeter Maydell <peter.maydell@linaro.org>2017-04-26 15:32:20 +0100
commit41c7c7ef2936722258c7ccf06027daca499c113d (patch)
tree4168bb3189be218465a9455a64f6a13cbc4a5936
parentdcaed66cbe2803f9378f3a043c2e482bf8035dfd (diff)
parent78f86a2b7c6b5c490a612729816473485d7ac570 (diff)
downloadqemu-41c7c7ef2936722258c7ccf06027daca499c113d.zip
qemu-41c7c7ef2936722258c7ccf06027daca499c113d.tar.gz
qemu-41c7c7ef2936722258c7ccf06027daca499c113d.tar.bz2
Merge remote-tracking branch 'remotes/dgilbert/tags/pull-hmp-20170426' into staging
HMP pull, with tcg fix # gpg: Signature made Wed 26 Apr 2017 14:55:30 BST # gpg: using RSA key 0x0516331EBC5BFDE7 # gpg: Good signature from "Dr. David Alan Gilbert (RH2) <dgilbert@redhat.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 45F5 C71B 4A0C B7FB 977A 9FA9 0516 331E BC5B FDE7 * remotes/dgilbert/tags/pull-hmp-20170426: tests: Add a tester for HMP commands libqtest: Add a generic function to run a callback function for every machine libqtest: Ignore QMP events when parsing the response for HMP commands monitor: Check whether TCG is enabled before running the "info jit" code hmp: gpa2hva and gpa2hpa hostaddr command Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--hmp-commands.hx32
-rw-r--r--monitor.c106
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/libqtest.c36
-rw-r--r--tests/libqtest.h12
-rw-r--r--tests/pc-cpu-test.c95
-rw-r--r--tests/qom-test.c36
-rw-r--r--tests/test-hmp.c161
8 files changed, 390 insertions, 90 deletions
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 8819281..0aca984 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -526,6 +526,38 @@ Dump 80 16 bit values at the start of the video memory.
ETEXI
{
+ .name = "gpa2hva",
+ .args_type = "addr:l",
+ .params = "addr",
+ .help = "print the host virtual address corresponding to a guest physical address",
+ .cmd = hmp_gpa2hva,
+ },
+
+STEXI
+@item gpa2hva @var{addr}
+@findex gpa2hva
+Print the host virtual address at which the guest's physical address @var{addr}
+is mapped.
+ETEXI
+
+#ifdef CONFIG_LINUX
+ {
+ .name = "gpa2hpa",
+ .args_type = "addr:l",
+ .params = "addr",
+ .help = "print the host physical address corresponding to a guest physical address",
+ .cmd = hmp_gpa2hpa,
+ },
+#endif
+
+STEXI
+@item gpa2hpa @var{addr}
+@findex gpa2hpa
+Print the host physical address at which the guest's physical address @var{addr}
+is mapped.
+ETEXI
+
+ {
.name = "p|print",
.args_type = "fmt:/,val:l",
.params = "/fmt expr",
diff --git a/monitor.c b/monitor.c
index be282ec..6289e52 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1086,6 +1086,11 @@ static void hmp_info_registers(Monitor *mon, const QDict *qdict)
static void hmp_info_jit(Monitor *mon, const QDict *qdict)
{
+ if (!tcg_enabled()) {
+ error_report("JIT information is only available with accel=tcg");
+ return;
+ }
+
dump_exec_info((FILE *)mon, monitor_fprintf);
dump_drift_info((FILE *)mon, monitor_fprintf);
}
@@ -1421,6 +1426,107 @@ static void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict)
memory_dump(mon, count, format, size, addr, 1);
}
+static void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, Error **errp)
+{
+ MemoryRegionSection mrs = memory_region_find(get_system_memory(),
+ addr, 1);
+
+ if (!mrs.mr) {
+ error_setg(errp, "No memory is mapped at address 0x%" HWADDR_PRIx, addr);
+ return NULL;
+ }
+
+ if (!memory_region_is_ram(mrs.mr) && !memory_region_is_romd(mrs.mr)) {
+ error_setg(errp, "Memory at address 0x%" HWADDR_PRIx "is not RAM", addr);
+ memory_region_unref(mrs.mr);
+ return NULL;
+ }
+
+ *p_mr = mrs.mr;
+ return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region);
+}
+
+static void hmp_gpa2hva(Monitor *mon, const QDict *qdict)
+{
+ hwaddr addr = qdict_get_int(qdict, "addr");
+ Error *local_err = NULL;
+ MemoryRegion *mr = NULL;
+ void *ptr;
+
+ ptr = gpa2hva(&mr, addr, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ return;
+ }
+
+ monitor_printf(mon, "Host virtual address for 0x%" HWADDR_PRIx
+ " (%s) is %p\n",
+ addr, mr->name, ptr);
+
+ memory_region_unref(mr);
+}
+
+#ifdef CONFIG_LINUX
+static uint64_t vtop(void *ptr, Error **errp)
+{
+ uint64_t pinfo;
+ uint64_t ret = -1;
+ uintptr_t addr = (uintptr_t) ptr;
+ uintptr_t pagesize = getpagesize();
+ off_t offset = addr / pagesize * sizeof(pinfo);
+ int fd;
+
+ fd = open("/proc/self/pagemap", O_RDONLY);
+ if (fd == -1) {
+ error_setg_errno(errp, errno, "Cannot open /proc/self/pagemap");
+ return -1;
+ }
+
+ /* Force copy-on-write if necessary. */
+ atomic_add((uint8_t *)ptr, 0);
+
+ if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) {
+ error_setg_errno(errp, errno, "Cannot read pagemap");
+ goto out;
+ }
+ if ((pinfo & (1ull << 63)) == 0) {
+ error_setg(errp, "Page not present");
+ goto out;
+ }
+ ret = ((pinfo & 0x007fffffffffffffull) * pagesize) | (addr & (pagesize - 1));
+
+out:
+ close(fd);
+ return ret;
+}
+
+static void hmp_gpa2hpa(Monitor *mon, const QDict *qdict)
+{
+ hwaddr addr = qdict_get_int(qdict, "addr");
+ Error *local_err = NULL;
+ MemoryRegion *mr = NULL;
+ void *ptr;
+ uint64_t physaddr;
+
+ ptr = gpa2hva(&mr, addr, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ return;
+ }
+
+ physaddr = vtop(ptr, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ } else {
+ monitor_printf(mon, "Host physical address for 0x%" HWADDR_PRIx
+ " (%s) is 0x%" PRIx64 "\n",
+ addr, mr->name, (uint64_t) physaddr);
+ }
+
+ memory_region_unref(mr);
+}
+#endif
+
static void do_print(Monitor *mon, const QDict *qdict)
{
int format = qdict_get_int(qdict, "format");
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 579ec07..31931c0 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -331,6 +331,7 @@ check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
check-qtest-s390x-y = tests/boot-serial-test$(EXESUF)
check-qtest-generic-y += tests/qom-test$(EXESUF)
+check-qtest-generic-y += tests/test-hmp$(EXESUF)
qapi-schema += alternate-any.json
qapi-schema += alternate-array.json
@@ -720,6 +721,7 @@ tests/tpci200-test$(EXESUF): tests/tpci200-test.o
tests/display-vga-test$(EXESUF): tests/display-vga-test.o
tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o
tests/qom-test$(EXESUF): tests/qom-test.o
+tests/test-hmp$(EXESUF): tests/test-hmp.o
tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-pc-obj-y)
tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y)
tests/nvme-test$(EXESUF): tests/nvme-test.o
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 99b1195..512c150 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -588,6 +588,12 @@ char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap)
" 'arguments': {'command-line': %s}}",
cmd);
ret = g_strdup(qdict_get_try_str(resp, "return"));
+ while (ret == NULL && qdict_get_try_str(resp, "event")) {
+ /* Ignore asynchronous QMP events */
+ QDECREF(resp);
+ resp = qtest_qmp_receive(s);
+ ret = g_strdup(qdict_get_try_str(resp, "return"));
+ }
g_assert(ret);
QDECREF(resp);
g_free(cmd);
@@ -940,3 +946,33 @@ bool qtest_big_endian(QTestState *s)
{
return s->big_endian;
}
+
+void qtest_cb_for_every_machine(void (*cb)(const char *machine))
+{
+ QDict *response, *minfo;
+ QList *list;
+ const QListEntry *p;
+ QObject *qobj;
+ QString *qstr;
+ const char *mname;
+
+ qtest_start("-machine none");
+ response = qmp("{ 'execute': 'query-machines' }");
+ g_assert(response);
+ list = qdict_get_qlist(response, "return");
+ g_assert(list);
+
+ for (p = qlist_first(list); p; p = qlist_next(p)) {
+ minfo = qobject_to_qdict(qlist_entry_obj(p));
+ g_assert(minfo);
+ qobj = qdict_get(minfo, "name");
+ g_assert(qobj);
+ qstr = qobject_to_qstring(qobj);
+ g_assert(qstr);
+ mname = qstring_get_str(qstr);
+ cb(mname);
+ }
+
+ qtest_end();
+ QDECREF(response);
+}
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 2c9962d..38bc1e9 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -132,11 +132,12 @@ void qtest_qmp_eventwait(QTestState *s, const char *event);
QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event);
/**
- * qtest_hmpv:
+ * qtest_hmp:
* @s: #QTestState instance to operate on.
* @fmt...: HMP command to send to QEMU
*
* Send HMP command to QEMU via QMP's human-monitor-command.
+ * QMP events are discarded.
*
* Returns: the command's output. The caller should g_free() it.
*/
@@ -149,6 +150,7 @@ char *qtest_hmp(QTestState *s, const char *fmt, ...);
* @ap: HMP command arguments
*
* Send HMP command to QEMU via QMP's human-monitor-command.
+ * QMP events are discarded.
*
* Returns: the command's output. The caller should g_free() it.
*/
@@ -917,4 +919,12 @@ void qmp_fd_send(int fd, const char *fmt, ...);
QDict *qmp_fdv(int fd, const char *fmt, va_list ap);
QDict *qmp_fd(int fd, const char *fmt, ...);
+/**
+ * qtest_cb_for_every_machine:
+ * @cb: Pointer to the callback function
+ *
+ * Call a callback function for every name of all available machines.
+ */
+void qtest_cb_for_every_machine(void (*cb)(const char *machine));
+
#endif
diff --git a/tests/pc-cpu-test.c b/tests/pc-cpu-test.c
index c3a2633..c4211a4 100644
--- a/tests/pc-cpu-test.c
+++ b/tests/pc-cpu-test.c
@@ -79,69 +79,46 @@ static void test_data_free(gpointer data)
g_free(pc);
}
-static void add_pc_test_cases(void)
+static void add_pc_test_case(const char *mname)
{
- QDict *response, *minfo;
- QList *list;
- const QListEntry *p;
- QObject *qobj;
- QString *qstr;
- const char *mname;
char *path;
PCTestData *data;
- qtest_start("-machine none");
- response = qmp("{ 'execute': 'query-machines' }");
- g_assert(response);
- list = qdict_get_qlist(response, "return");
- g_assert(list);
-
- for (p = qlist_first(list); p; p = qlist_next(p)) {
- minfo = qobject_to_qdict(qlist_entry_obj(p));
- g_assert(minfo);
- qobj = qdict_get(minfo, "name");
- g_assert(qobj);
- qstr = qobject_to_qstring(qobj);
- g_assert(qstr);
- mname = qstring_get_str(qstr);
- if (!g_str_has_prefix(mname, "pc-")) {
- continue;
- }
- data = g_malloc(sizeof(PCTestData));
- data->machine = g_strdup(mname);
- data->cpu_model = "Haswell"; /* 1.3+ theoretically */
- data->sockets = 1;
- data->cores = 3;
- data->threads = 2;
- data->maxcpus = data->sockets * data->cores * data->threads * 2;
- if (g_str_has_suffix(mname, "-1.4") ||
- (strcmp(mname, "pc-1.3") == 0) ||
- (strcmp(mname, "pc-1.2") == 0) ||
- (strcmp(mname, "pc-1.1") == 0) ||
- (strcmp(mname, "pc-1.0") == 0) ||
- (strcmp(mname, "pc-0.15") == 0) ||
- (strcmp(mname, "pc-0.14") == 0) ||
- (strcmp(mname, "pc-0.13") == 0) ||
- (strcmp(mname, "pc-0.12") == 0) ||
- (strcmp(mname, "pc-0.11") == 0) ||
- (strcmp(mname, "pc-0.10") == 0)) {
- path = g_strdup_printf("cpu/%s/init/%ux%ux%u&maxcpus=%u",
- mname, data->sockets, data->cores,
- data->threads, data->maxcpus);
- qtest_add_data_func_full(path, data, test_pc_without_cpu_add,
- test_data_free);
- g_free(path);
- } else {
- path = g_strdup_printf("cpu/%s/add/%ux%ux%u&maxcpus=%u",
- mname, data->sockets, data->cores,
- data->threads, data->maxcpus);
- qtest_add_data_func_full(path, data, test_pc_with_cpu_add,
- test_data_free);
- g_free(path);
- }
+ if (!g_str_has_prefix(mname, "pc-")) {
+ return;
+ }
+ data = g_malloc(sizeof(PCTestData));
+ data->machine = g_strdup(mname);
+ data->cpu_model = "Haswell"; /* 1.3+ theoretically */
+ data->sockets = 1;
+ data->cores = 3;
+ data->threads = 2;
+ data->maxcpus = data->sockets * data->cores * data->threads * 2;
+ if (g_str_has_suffix(mname, "-1.4") ||
+ (strcmp(mname, "pc-1.3") == 0) ||
+ (strcmp(mname, "pc-1.2") == 0) ||
+ (strcmp(mname, "pc-1.1") == 0) ||
+ (strcmp(mname, "pc-1.0") == 0) ||
+ (strcmp(mname, "pc-0.15") == 0) ||
+ (strcmp(mname, "pc-0.14") == 0) ||
+ (strcmp(mname, "pc-0.13") == 0) ||
+ (strcmp(mname, "pc-0.12") == 0) ||
+ (strcmp(mname, "pc-0.11") == 0) ||
+ (strcmp(mname, "pc-0.10") == 0)) {
+ path = g_strdup_printf("cpu/%s/init/%ux%ux%u&maxcpus=%u",
+ mname, data->sockets, data->cores,
+ data->threads, data->maxcpus);
+ qtest_add_data_func_full(path, data, test_pc_without_cpu_add,
+ test_data_free);
+ g_free(path);
+ } else {
+ path = g_strdup_printf("cpu/%s/add/%ux%ux%u&maxcpus=%u",
+ mname, data->sockets, data->cores,
+ data->threads, data->maxcpus);
+ qtest_add_data_func_full(path, data, test_pc_with_cpu_add,
+ test_data_free);
+ g_free(path);
}
- QDECREF(response);
- qtest_end();
}
int main(int argc, char **argv)
@@ -151,7 +128,7 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
- add_pc_test_cases();
+ qtest_cb_for_every_machine(add_pc_test_case);
}
return g_test_run();
diff --git a/tests/qom-test.c b/tests/qom-test.c
index d48f890..ab0595d 100644
--- a/tests/qom-test.c
+++ b/tests/qom-test.c
@@ -107,46 +107,22 @@ static void test_machine(gconstpointer data)
g_free((void *)machine);
}
-static void add_machine_test_cases(void)
+static void add_machine_test_case(const char *mname)
{
const char *arch = qtest_get_arch();
- QDict *response, *minfo;
- QList *list;
- const QListEntry *p;
- QObject *qobj;
- QString *qstr;
- const char *mname;
- qtest_start("-machine none");
- response = qmp("{ 'execute': 'query-machines' }");
- g_assert(response);
- list = qdict_get_qlist(response, "return");
- g_assert(list);
-
- for (p = qlist_first(list); p; p = qlist_next(p)) {
- minfo = qobject_to_qdict(qlist_entry_obj(p));
- g_assert(minfo);
- qobj = qdict_get(minfo, "name");
- g_assert(qobj);
- qstr = qobject_to_qstring(qobj);
- g_assert(qstr);
- mname = qstring_get_str(qstr);
- if (!is_blacklisted(arch, mname)) {
- char *path = g_strdup_printf("qom/%s", mname);
- qtest_add_data_func(path, g_strdup(mname), test_machine);
- g_free(path);
- }
+ if (!is_blacklisted(arch, mname)) {
+ char *path = g_strdup_printf("qom/%s", mname);
+ qtest_add_data_func(path, g_strdup(mname), test_machine);
+ g_free(path);
}
-
- qtest_end();
- QDECREF(response);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
- add_machine_test_cases();
+ qtest_cb_for_every_machine(add_machine_test_case);
return g_test_run();
}
diff --git a/tests/test-hmp.c b/tests/test-hmp.c
new file mode 100644
index 0000000..99e35ec
--- /dev/null
+++ b/tests/test-hmp.c
@@ -0,0 +1,161 @@
+/*
+ * Test HMP commands.
+ *
+ * Copyright (c) 2017 Red Hat Inc.
+ *
+ * Author:
+ * Thomas Huth <thuth@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2
+ * or later. See the COPYING file in the top-level directory.
+ *
+ * This test calls some HMP commands for all machines that the current
+ * QEMU binary provides, to check whether they terminate successfully
+ * (i.e. do not crash QEMU).
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+static int verbose;
+
+static const char *hmp_cmds[] = {
+ "boot_set ndc",
+ "chardev-add null,id=testchardev1",
+ "chardev-remove testchardev1",
+ "commit all",
+ "cpu-add 1",
+ "cpu 0",
+ "device_add ?",
+ "device_add usb-mouse,id=mouse1",
+ "mouse_button 7",
+ "mouse_move 10 10",
+ "mouse_button 0",
+ "device_del mouse1",
+ "dump-guest-memory /dev/null 0 4096",
+ "gdbserver",
+ "host_net_add user id=net0",
+ "hostfwd_add tcp::43210-:43210",
+ "hostfwd_remove tcp::43210-:43210",
+ "host_net_remove 0 net0",
+ "i /w 0",
+ "log all",
+ "log none",
+ "memsave 0 4096 \"/dev/null\"",
+ "migrate_set_cache_size 1",
+ "migrate_set_downtime 1",
+ "migrate_set_speed 1",
+ "netdev_add user,id=net1",
+ "set_link net1 off",
+ "set_link net1 on",
+ "netdev_del net1",
+ "nmi",
+ "o /w 0 0x1234",
+ "object_add memory-backend-ram,id=mem1,size=256M",
+ "object_del mem1",
+ "pmemsave 0 4096 \"/dev/null\"",
+ "p $pc + 8",
+ "qom-list /",
+ "qom-set /machine initrd test",
+ "screendump /dev/null",
+ "sendkey x",
+ "singlestep on",
+ "wavcapture /dev/null",
+ "stopcapture 0",
+ "sum 0 512",
+ "x /8i 0x100",
+ "xp /16x 0",
+ NULL
+};
+
+/* Run through the list of pre-defined commands */
+static void test_commands(void)
+{
+ char *response;
+ int i;
+
+ for (i = 0; hmp_cmds[i] != NULL; i++) {
+ if (verbose) {
+ fprintf(stderr, "\t%s\n", hmp_cmds[i]);
+ }
+ response = hmp(hmp_cmds[i]);
+ g_free(response);
+ }
+
+}
+
+/* Run through all info commands and call them blindly (without arguments) */
+static void test_info_commands(void)
+{
+ char *resp, *info, *info_buf, *endp;
+
+ info_buf = info = hmp("help info");
+
+ while (*info) {
+ /* Extract the info command, ignore parameters and description */
+ g_assert(strncmp(info, "info ", 5) == 0);
+ endp = strchr(&info[5], ' ');
+ g_assert(endp != NULL);
+ *endp = '\0';
+ /* Now run the info command */
+ if (verbose) {
+ fprintf(stderr, "\t%s\n", info);
+ }
+ resp = hmp(info);
+ g_free(resp);
+ /* And move forward to the next line */
+ info = strchr(endp + 1, '\n');
+ if (!info) {
+ break;
+ }
+ info += 1;
+ }
+
+ g_free(info_buf);
+}
+
+static void test_machine(gconstpointer data)
+{
+ const char *machine = data;
+ char *args;
+
+ args = g_strdup_printf("-S -M %s", machine);
+ qtest_start(args);
+
+ test_info_commands();
+ test_commands();
+
+ qtest_end();
+ g_free(args);
+ g_free((void *)data);
+}
+
+static void add_machine_test_case(const char *mname)
+{
+ char *path;
+
+ /* Ignore blacklisted machines that have known problems */
+ if (!strcmp("puv3", mname) || !strcmp("tricore_testboard", mname) ||
+ !strcmp("xenfv", mname) || !strcmp("xenpv", mname)) {
+ return;
+ }
+
+ path = g_strdup_printf("hmp/%s", mname);
+ qtest_add_data_func(path, g_strdup(mname), test_machine);
+ g_free(path);
+}
+
+int main(int argc, char **argv)
+{
+ char *v_env = getenv("V");
+
+ if (v_env && *v_env >= '2') {
+ verbose = true;
+ }
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_cb_for_every_machine(add_machine_test_case);
+
+ return g_test_run();
+}