From: "Bryan D. Payne" <bdpayne@acm.org>
To: qemu-devel@nongnu.org
Cc: bdpayne@acm.org, lcapitulino@redhat.com
Subject: [Qemu-devel] [PATCH] qmp: extend QMP to provide read/write access to physical memory
Date: Wed, 26 Nov 2014 12:27:47 -0800 [thread overview]
Message-ID: <1417033667-10364-2-git-send-email-bdpayne@acm.org> (raw)
In-Reply-To: <1417033667-10364-1-git-send-email-bdpayne@acm.org>
This patch adds a new QMP command that sets up a domain socket. This
socket can then be used for fast read/write access to the guest's
physical memory. The key benefit to this system over existing solutions
is speed. Using this patch, guest memory can be copied out at a rate of
~200MB/sec, depending on the hardware. Existing solutions only achieve
a small fraction of this speed.
Signed-off-by: Bryan D. Payne <bdpayne@acm.org>
---
Makefile.target | 2 +-
memory-access.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
memory-access.h | 11 +++
monitor.c | 6 ++
qapi-schema.json | 12 ++++
qmp-commands.hx | 28 ++++++++
6 files changed, 258 insertions(+), 1 deletion(-)
create mode 100644 memory-access.c
create mode 100644 memory-access.h
diff --git a/Makefile.target b/Makefile.target
index e9ff1ee..4b3cd99 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -127,7 +127,7 @@ endif #CONFIG_BSD_USER
# System emulator target
ifdef CONFIG_SOFTMMU
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
-obj-y += qtest.o bootdevice.o
+obj-y += qtest.o bootdevice.o memory-access.o
obj-y += hw/
obj-$(CONFIG_FDT) += device_tree.o
obj-$(CONFIG_KVM) += kvm-all.o
diff --git a/memory-access.c b/memory-access.c
new file mode 100644
index 0000000..f696d7b
--- /dev/null
+++ b/memory-access.c
@@ -0,0 +1,200 @@
+/*
+ * Access guest physical memory via a domain socket.
+ *
+ * Copyright (c) 2014 Bryan D. Payne (bdpayne@acm.org)
+ */
+
+#include "memory-access.h"
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "config.h"
+
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdint.h>
+
+struct request {
+ uint8_t type; /* 0 quit, 1 read, 2 write, ... rest reserved */
+ uint64_t address; /* address to read from OR write to */
+ uint64_t length; /* number of bytes to read OR write */
+};
+
+static uint64_t
+connection_read_memory(uint64_t user_paddr, void *buf, uint64_t user_len)
+{
+ hwaddr paddr = (hwaddr) user_paddr;
+ hwaddr len = (hwaddr) user_len;
+ void *guestmem = cpu_physical_memory_map(paddr, &len, 0);
+ if (!guestmem) {
+ return 0;
+ }
+ memcpy(buf, guestmem, len);
+ cpu_physical_memory_unmap(guestmem, len, 0, len);
+
+ return len;
+}
+
+static uint64_t
+connection_write_memory(uint64_t user_paddr,
+ const void *buf,
+ uint64_t user_len)
+{
+ hwaddr paddr = (hwaddr) user_paddr;
+ hwaddr len = (hwaddr) user_len;
+ void *guestmem = cpu_physical_memory_map(paddr, &len, 1);
+ if (!guestmem) {
+ return 0;
+ }
+ memcpy(guestmem, buf, len);
+ cpu_physical_memory_unmap(guestmem, len, 0, len);
+
+ return len;
+}
+
+static void
+send_success_ack(int connection_fd)
+{
+ uint8_t success = 1;
+ int nbytes = write(connection_fd, &success, 1);
+ if (nbytes != 1) {
+ printf("QemuMemoryAccess: failed to send success ack\n");
+ }
+}
+
+static void
+send_fail_ack(int connection_fd)
+{
+ uint8_t fail = 0;
+ int nbytes = write(connection_fd, &fail, 1);
+ if (nbytes != 1) {
+ printf("QemuMemoryAccess: failed to send fail ack\n");
+ }
+}
+
+static void
+connection_handler(int connection_fd)
+{
+ int nbytes;
+ struct request req;
+
+ while (1) {
+ /* client request should match the struct request format */
+ nbytes = read(connection_fd, &req, sizeof(struct request));
+ if (nbytes != sizeof(struct request)) {
+ /* error */
+ send_fail_ack(connection_fd);
+ continue;
+ } else if (req.type == 0) {
+ /* request to quit, goodbye */
+ break;
+ } else if (req.type == 1) {
+ /* request to read */
+ char *buf = g_malloc(req.length + 1);
+ nbytes = connection_read_memory(req.address, buf, req.length);
+ if (nbytes != req.length) {
+ /* read failure, return failure message */
+ buf[req.length] = 0; /* set last byte to 0 for failure */
+ nbytes = write(connection_fd, buf, 1);
+ } else {
+ /* read success, return bytes */
+ buf[req.length] = 1; /* set last byte to 1 for success */
+ nbytes = write(connection_fd, buf, nbytes + 1);
+ }
+ g_free(buf);
+ } else if (req.type == 2) {
+ /* request to write */
+ void *write_buf = g_malloc(req.length);
+ nbytes = read(connection_fd, &write_buf, req.length);
+ if (nbytes != req.length) {
+ /* failed reading the message to write */
+ send_fail_ack(connection_fd);
+ } else{
+ /* do the write */
+ nbytes = connection_write_memory(req.address,
+ write_buf,
+ req.length);
+ if (nbytes == req.length) {
+ send_success_ack(connection_fd);
+ } else {
+ send_fail_ack(connection_fd);
+ }
+ }
+ g_free(write_buf);
+ } else {
+ /* unknown command */
+ printf("QemuMemoryAccess: ignoring unknown command (%d)\n",
+ req.type);
+ send_fail_ack(connection_fd);
+ }
+ }
+
+ close(connection_fd);
+}
+
+static void *
+memory_access_thread(void *path)
+{
+ struct sockaddr_un address;
+ int socket_fd, connection_fd, path_len;
+ socklen_t address_length;
+
+ socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (socket_fd < 0) {
+ printf("QemuMemoryAccess: socket failed\n");
+ goto error_exit;
+ }
+
+ unlink(path); /* handle unlikely case that this temp file exists */
+ memset(&address, 0, sizeof(struct sockaddr_un));
+ address.sun_family = AF_UNIX;
+ path_len = sprintf(address.sun_path, "%s", (char *) path);
+ address_length = sizeof(address.sun_family) + path_len;
+
+ if (bind(socket_fd, (struct sockaddr *) &address, address_length) != 0) {
+ printf("QemuMemoryAccess: bind failed\n");
+ goto error_exit;
+ }
+ if (listen(socket_fd, 0) != 0) {
+ printf("QemuMemoryAccess: listen failed\n");
+ goto error_exit;
+ }
+
+ connection_fd = accept(socket_fd,
+ (struct sockaddr *) &address,
+ &address_length);
+ connection_handler(connection_fd);
+
+ close(socket_fd);
+ unlink(path);
+error_exit:
+ g_free(path);
+ return NULL;
+}
+
+int
+memory_access_start(const char *path)
+{
+ pthread_t thread;
+ sigset_t set, oldset;
+ int ret;
+
+ /* create a copy of path that we can safely use */
+ char *pathcopy = g_malloc(strlen(path) + 1);
+ memcpy(pathcopy, path, strlen(path) + 1);
+
+ /* start the thread */
+ sigfillset(&set);
+ pthread_sigmask(SIG_SETMASK, &set, &oldset);
+ ret = pthread_create(&thread, NULL, memory_access_thread, pathcopy);
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+
+ return ret;
+}
diff --git a/memory-access.h b/memory-access.h
new file mode 100644
index 0000000..cb5fe33
--- /dev/null
+++ b/memory-access.h
@@ -0,0 +1,11 @@
+/*
+ * Access guest physical memory via a domain socket.
+ *
+ * Copyright (c) 2014 Bryan D. Payne (bdpayne@acm.org)
+ */
+#ifndef MEMORY_ACCESS_H
+#define MEMORY_ACCESS_H
+
+int memory_access_start(const char *path);
+
+#endif /* MEMORY_ACCESS_H */
diff --git a/monitor.c b/monitor.c
index fa00594..19724b8 100644
--- a/monitor.c
+++ b/monitor.c
@@ -72,6 +72,7 @@
#include "block/qapi.h"
#include "qapi/qmp-event.h"
#include "qapi-event.h"
+#include "memory-access.h"
/* for pic/irq_info */
#if defined(TARGET_SPARC)
@@ -1371,6 +1372,11 @@ static void do_print(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "\n");
}
+void qmp_pmemaccess(const char *path, Error **err)
+{
+ memory_access_start(path);
+}
+
static void do_sum(Monitor *mon, const QDict *qdict)
{
uint32_t addr;
diff --git a/qapi-schema.json b/qapi-schema.json
index 9ffdcf8..37a6657 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3515,3 +3515,15 @@
# Since: 2.1
##
{ 'command': 'rtc-reset-reinjection' }
+
+##
+# @pmemaccess
+#
+# This command enables access to guest physical memory using
+# a simple protocol over a UNIX domain socket.
+#
+# @path Location to use for the UNIX domain socket
+#
+# Since: 2.3
+##
+{ 'command': 'pmemaccess', 'data': { 'path': 'str' } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 718dd92..fab1322 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -609,6 +609,34 @@ Example:
EQMP
{
+ .name = "pmemaccess",
+ .args_type = "path:s",
+ .params = "path",
+ .help = "access to guest memory via domain socket at 'path'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = qmp_marshal_input_pmemaccess,
+ },
+
+SQMP
+pmemaccess
+----------
+
+This command enables access to guest physical memory using a simple protocol
+over a UNIX domain socket.
+
+Arguments:
+
+- "path": path for domain socket (json-string)
+
+Example:
+
+-> { "execute": "pmemaccess",
+ "arguments": { "path": "/tmp/guestname" } }
+<- { "return": {} }
+
+EQMP
+
+ {
.name = "migrate",
.args_type = "detach:-d,blk:-b,inc:-i,uri:s",
.mhandler.cmd_new = qmp_marshal_input_migrate,
--
1.9.1
next prev parent reply other threads:[~2014-11-26 20:28 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-11-26 8:41 [Qemu-devel] [PATCH 0/1] qmp: extend QMP to provide read/write access to physical memory Bryan D. Payne
2014-11-26 8:41 ` [Qemu-devel] [PATCH 1/1] " Bryan D. Payne
2014-11-26 15:16 ` Eric Blake
2014-11-26 20:27 ` [Qemu-devel] [PATCH v2] " Bryan D. Payne
2014-11-26 20:27 ` Bryan D. Payne [this message]
2014-11-27 2:04 ` [Qemu-devel] [PATCH] " Fam Zheng
2014-12-04 3:37 ` Bryan D. Payne
2014-12-04 4:57 ` Fam Zheng
2014-12-04 6:28 ` Bryan D. Payne
2014-12-04 7:38 ` Fam Zheng
2014-12-04 16:43 ` Bryan D. Payne
2014-12-05 1:20 ` Fam Zheng
2014-12-04 9:08 ` Markus Armbruster
2014-12-04 16:49 ` Bryan D. Payne
2014-12-05 8:44 ` Markus Armbruster
2014-12-05 21:25 ` Bryan D. Payne
2014-12-08 15:06 ` Markus Armbruster
2014-12-09 15:12 ` Bryan D. Payne
2014-12-11 3:33 ` Bryan D. Payne
2014-12-11 5:45 ` Fam Zheng
2014-12-11 6:07 ` Bryan D. Payne
2014-12-12 2:28 ` Bryan D. Payne
2014-12-12 3:29 ` Fam Zheng
2014-12-01 22:10 ` Eric Blake
2014-12-03 23:07 ` Bryan D. Payne
2014-12-04 15:08 ` Eric Blake
2014-12-04 16:50 ` Bryan D. Payne
2014-12-04 18:40 ` Bryan D. Payne
2014-12-04 22:43 ` Eric Blake
2014-12-01 22:12 ` [Qemu-devel] [PATCH v2] " Eric Blake
2014-12-02 4:36 ` Bryan D. Payne
2014-12-02 5:26 ` Fam Zheng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1417033667-10364-2-git-send-email-bdpayne@acm.org \
--to=bdpayne@acm.org \
--cc=lcapitulino@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).