[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] qmp: extend QMP to provide read/write access to phy
From: |
Bryan D. Payne |
Subject: |
[Qemu-devel] [PATCH] qmp: extend QMP to provide read/write access to physical memory |
Date: |
Wed, 26 Nov 2014 12:27:47 -0800 |
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 <address@hidden>
---
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 (address@hidden)
+ */
+
+#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 (address@hidden)
+ */
+#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