From: Valerio Aimale <vale...@aimale.com>

---
 Makefile.target  |   2 +-
 hmp-commands.hx  |  14 ++++
 hmp.c            |   9 +++
 hmp.h            |   1 +
 memory-access.c  | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 memory-access.h  |  21 ++++++
 qapi-schema.json |  28 ++++++++
 qmp-commands.hx  |  23 +++++++
 8 files changed, 303 insertions(+), 1 deletion(-)
 create mode 100644 memory-access.c
 create mode 100644 memory-access.h

diff --git a/Makefile.target b/Makefile.target
index 962d004..940ab51 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -131,7 +131,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 += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o 
memory-access.o
 obj-y += qtest.o bootdevice.o
 obj-y += hw/
 obj-$(CONFIG_KVM) += kvm-all.o
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 3a4ae39..37d3bb5 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -807,6 +807,20 @@ save to disk physical memory dump starting at @var{addr} 
of size @var{size}.
 ETEXI
 
     {
+        .name       = "pmemaccess",
+        .args_type  = "path:s",
+        .params     = "file",
+        .help       = "open A UNIX Socket access to physical memory at 'path'",
+        .mhandler.cmd = hmp_pmemaccess,
+    },
+
+STEXI
+@item pmemaccess @var{file}
+@findex pmemaccess
+Mount guest physical memory image at 'path'
+ETEXI
+
+    {
         .name       = "boot_set",
         .args_type  = "bootdevice:s",
         .params     = "bootdevice",
diff --git a/hmp.c b/hmp.c
index 5048eee..444dbf6 100644
--- a/hmp.c
+++ b/hmp.c
@@ -916,6 +916,15 @@ void hmp_pmemsave(Monitor *mon, const QDict *qdict)
     hmp_handle_error(mon, &err);
 }
 
+void hmp_pmemaccess(Monitor *mon, const QDict *qdict)
+{
+    const char *path = qdict_get_str(qdict, "path");
+    Error *err = NULL;
+
+    qmp_pmemaccess(path, &err);
+    hmp_handle_error(mon, &err);
+}
+
 void hmp_ringbuf_write(Monitor *mon, const QDict *qdict)
 {
     const char *chardev = qdict_get_str(qdict, "device");
diff --git a/hmp.h b/hmp.h
index 81656c3..8225deb 100644
--- a/hmp.h
+++ b/hmp.h
@@ -47,6 +47,7 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdict);
 void hmp_cpu(Monitor *mon, const QDict *qdict);
 void hmp_memsave(Monitor *mon, const QDict *qdict);
 void hmp_pmemsave(Monitor *mon, const QDict *qdict);
+void hmp_pmemaccess(Monitor *mon, const QDict *qdict);
 void hmp_ringbuf_write(Monitor *mon, const QDict *qdict);
 void hmp_ringbuf_read(Monitor *mon, const QDict *qdict);
 void hmp_cont(Monitor *mon, const QDict *qdict);
diff --git a/memory-access.c b/memory-access.c
new file mode 100644
index 0000000..58099a8
--- /dev/null
+++ b/memory-access.c
@@ -0,0 +1,206 @@
+/*
+ * Access guest physical memory via a domain socket.
+ *
+ * Copyright (C) 2011 Sandia National Laboratories
+ * Original Author: Bryan D. Payne (bdpa...@acm.org)
+ * 
+ * Refurbished for modern QEMU by Valerio Aimale (vale...@aimale.com), in 2015
+ */
+
+#include "memory-access.h"
+//#include "cpu-all.h"
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "config.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, 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 (1 != nbytes){
+        fprintf(stderr, "Qemu pmemaccess: 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 (1 != nbytes){
+        fprintf(stderr, "Qemu pmemaccess: 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
+            continue;
+        }
+        else if (req.type == 0){
+            // request to quit, goodbye
+            break;
+        }
+        else if (req.type == 1){
+            // request to read
+            char *buf = 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);
+            }
+            free(buf);
+        }
+        else if (req.type == 2){
+            // request to write
+            void *write_buf = 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);
+                }
+            }
+            free(write_buf);
+        }
+        else{
+            // unknown command
+            fprintf(stderr, "Qemu pmemaccess: ignoring unknown command 
(%d)\n", req.type);
+            char *buf = malloc(1);
+            buf[0] = 0;
+            nbytes = write(connection_fd, buf, 1);
+            free(buf);
+        }
+    }
+
+    close(connection_fd);
+}
+
+static void *
+memory_access_thread (void *p)
+{
+    struct sockaddr_un address;
+    int socket_fd, connection_fd;
+    socklen_t address_length;
+    struct pmemaccess_args *pargs = (struct pmemaccess_args *)p;
+
+    socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+    if (socket_fd < 0){
+       error_setg(pargs->errp, "Qemu pmemaccess: socket failed");
+        goto error_exit;
+    }
+    unlink(pargs->path);
+    address.sun_family = AF_UNIX;
+    address_length = sizeof(address.sun_family) + sprintf(address.sun_path, 
"%s", (char *) pargs->path);
+
+    if (bind(socket_fd, (struct sockaddr *) &address, address_length) != 0){
+       error_setg(pargs->errp, "Qemu pmemaccess: bind failed");
+        goto error_exit;
+    }
+    if (listen(socket_fd, 0) != 0){
+       error_setg(pargs->errp, "Qemu pmemaccess: listen failed");
+        goto error_exit;
+    }
+
+    connection_fd = accept(socket_fd, (struct sockaddr *) &address, 
&address_length);
+    connection_handler(connection_fd);
+
+    close(socket_fd);
+    unlink(pargs->path);
+    free(pargs->path);
+    free(pargs);
+error_exit:
+    return NULL;
+}
+
+void
+qmp_pmemaccess (const char *path, Error **errp)
+{
+    pthread_t thread;
+    sigset_t set, oldset;
+    struct pmemaccess_args *pargs;
+
+    // create the args struct
+    pargs = (struct pmemaccess_args *) malloc(sizeof(struct pmemaccess_args));
+    pargs->errp = errp;
+    // create a copy of path that we can safely use
+    pargs->path = malloc(strlen(path) + 1);
+    memcpy(pargs->path, path, strlen(path) + 1);
+       
+    // start the thread
+    sigfillset(&set);
+    pthread_sigmask(SIG_SETMASK, &set, &oldset);
+    pthread_create(&thread, NULL, memory_access_thread, pargs);
+    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+
+}
diff --git a/memory-access.h b/memory-access.h
new file mode 100644
index 0000000..ff3d2f9
--- /dev/null
+++ b/memory-access.h
@@ -0,0 +1,21 @@
+/*
+ * Mount guest physical memory using FUSE.
+ *
+ * Author: Valerio G. Aimale <vale...@aimale.com>
+ */
+
+#ifndef MEMORY_ACCESS_H
+#define MEMORY_ACCESS_H
+
+#include "qapi-types.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/error.h"
+
+void qmp_pmemaccess (const char *path, Error **errp);
+
+struct pmemaccess_args {
+       char *path;
+       Error **errp;
+};
+
+#endif /* MEMORY_ACCESS_H */
diff --git a/qapi-schema.json b/qapi-schema.json
index a386605..24d4070 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1427,6 +1427,34 @@
   'data': {'val': 'int', 'size': 'int', 'filename': 'str'} }
 
 ##
+# @pmemaccess:
+#
+# Open A UNIX Socket access to physical memory
+#
+# @path: the name of the UNIX socket pipe
+#
+# Returns: Nothing on success
+#
+# Since: 2.4.0.1
+#
+# Notes: Derived from previously existing patches. When command
+# succeeds connect to the open socket. Write a binary structure to 
+# the socket as:
+# 
+# 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
+# };
+# 
+# If it is a read operation, Qemu will return lenght+1 bytes. Read lenght+1
+# bytes. the last byte will be a 1 for success, or a 0 for failure.
+#  
+##
+{ 'command': 'pmemaccess',
+  'data': {'path': 'str'} }
+
+##
 # @cont:
 #
 # Resume guest VCPU execution.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index d2ba800..26e4a51 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -472,6 +472,29 @@ Example:
 EQMP
 
     {
+        .name       = "pmemaccess",
+        .args_type  = "path:s",
+        .mhandler.cmd_new = qmp_marshal_input_pmemaccess,
+    },
+
+SQMP
+pmemaccess
+----------
+
+Open A UNIX Socket access to physical memory
+
+Arguments:
+
+- "path": mount point path (json-string)
+
+Example:
+
+-> { "execute": "pmemaccess",
+             "arguments": { "path": "/tmp/guestname" } }
+<- { "return": {} }
+
+EQMP
+    {
         .name       = "inject-nmi",
         .args_type  = "",
         .mhandler.cmd_new = qmp_marshal_inject_nmi,
-- 
2.1.4


Reply via email to