This patch implements virDomainMemoryPeek, which allows callers to peek into guest (virtual) memory. Currently the patch contains a QEMU driver and remote support. There's also a big comment in the source describing how to do Xen. Obviously the use case for this is virt-mem: http://et.redhat.com/~rjones/virt-mem/ Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones Read my OCaml programming blog: http://camltastic.blogspot.com/ Fedora now supports 59 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora
Index: docs/hvsupport.html.in =================================================================== RCS file: /data/cvs/libvirt/docs/hvsupport.html.in,v retrieving revision 1.2 diff -u -p -r1.2 hvsupport.html.in --- docs/hvsupport.html.in 5 Jun 2008 13:17:45 -0000 1.2 +++ docs/hvsupport.html.in 5 Jun 2008 21:26:24 -0000 @@ -145,9 +145,9 @@ updated on <i>2008-06-05</i>. <tr> <td> virDomainBlockPeek </td> <td> 0.4.3 </td> - <td> x </td> - <td> x </td> - <td> x </td> + <td> 0.4.3 </td> + <td> 0.4.3 </td> + <td> 0.4.3 </td> <td> x </td> </tr> <tr> @@ -487,6 +487,14 @@ updated on <i>2008-06-05</i>. <td colspan="4"> not a HV function </td> </tr> <tr> + <td> virDomainMemoryPeek </td> + <td> 0.4.3 </td> + <td> x </td> + <td> 0.4.3 </td> + <td> 0.4.3 </td> + <td> x </td> + </tr> + <tr> <td> virNodeGetInfo </td> <td> 0.1.0 </td> <td> ≥ 0.1.0 </td> Index: include/libvirt/libvirt.h =================================================================== RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h,v retrieving revision 1.75 diff -u -p -r1.75 libvirt.h --- include/libvirt/libvirt.h 5 Jun 2008 13:17:45 -0000 1.75 +++ include/libvirt/libvirt.h 5 Jun 2008 21:26:25 -0000 @@ -537,6 +537,17 @@ int virDomainBlockPe void *buffer, unsigned int flags); +/* Memory peeking flags. */ +typedef enum { + VIR_MEMORY_VIRTUAL = 1, /* addresses are virtual addresses */ +} virDomainMemoryFlags; + +int virDomainMemoryPeek (virDomainPtr dom, + unsigned long long start, + size_t size, + void *buffer, + unsigned int flags); + /* * defined but not running domains */ Index: include/libvirt/libvirt.h.in =================================================================== RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h.in,v retrieving revision 1.50 diff -u -p -r1.50 libvirt.h.in --- include/libvirt/libvirt.h.in 5 Jun 2008 13:17:45 -0000 1.50 +++ include/libvirt/libvirt.h.in 5 Jun 2008 21:26:26 -0000 @@ -537,6 +537,17 @@ int virDomainBlockPe void *buffer, unsigned int flags); +/* Memory peeking flags. */ +typedef enum { + VIR_MEMORY_VIRTUAL = 1, /* addresses are virtual addresses */ +} virDomainMemoryFlags; + +int virDomainMemoryPeek (virDomainPtr dom, + unsigned long long start, + size_t size, + void *buffer, + unsigned int flags); + /* * defined but not running domains */ Index: qemud/remote.c =================================================================== RCS file: /data/cvs/libvirt/qemud/remote.c,v retrieving revision 1.36 diff -u -p -r1.36 remote.c --- qemud/remote.c 5 Jun 2008 21:12:26 -0000 1.36 +++ qemud/remote.c 5 Jun 2008 21:26:29 -0000 @@ -938,6 +938,52 @@ remoteDispatchDomainBlockPeek (struct qe } static int +remoteDispatchDomainMemoryPeek (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_domain_memory_peek_args *args, + remote_domain_memory_peek_ret *ret) +{ + virDomainPtr dom; + unsigned long long offset; + size_t size; + unsigned int flags; + CHECK_CONN (client); + + dom = get_nonnull_domain (client->conn, args->dom); + if (dom == NULL) { + remoteDispatchError (client, req, "%s", _("domain not found")); + return -2; + } + offset = args->offset; + size = args->size; + flags = args->flags; + + if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) { + remoteDispatchError (client, req, + "%s", _("size > maximum buffer size")); + return -2; + } + + ret->buffer.buffer_len = size; + ret->buffer.buffer_val = malloc (size); + if (!ret->buffer.buffer_val) { + remoteDispatchError (client, req, "%s", strerror (errno)); + return -2; + } + + if (virDomainMemoryPeek (dom, offset, size, + ret->buffer.buffer_val, flags) == -1) { + /* free (ret->buffer.buffer_val); - caller frees */ + virDomainFree (dom); + return -1; + } + virDomainFree (dom); + + return 0; +} + +static int remoteDispatchDomainAttachDevice (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client, remote_message_header *req, Index: qemud/remote_protocol.x =================================================================== RCS file: /data/cvs/libvirt/qemud/remote_protocol.x,v retrieving revision 1.14 diff -u -p -r1.14 remote_protocol.x --- qemud/remote_protocol.x 5 Jun 2008 21:12:27 -0000 1.14 +++ qemud/remote_protocol.x 5 Jun 2008 21:26:29 -0000 @@ -340,6 +340,17 @@ struct remote_domain_block_peek_ret { opaque buffer<REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX>; }; +struct remote_domain_memory_peek_args { + remote_nonnull_domain dom; + unsigned hyper offset; + unsigned size; + unsigned flags; +}; + +struct remote_domain_memory_peek_ret { + opaque buffer<REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX>; +}; + struct remote_list_domains_args { int maxids; }; @@ -1056,7 +1067,8 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_CELLS_FREE_MEMORY = 101, REMOTE_PROC_NODE_GET_FREE_MEMORY = 102, - REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103 + REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103, + REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104 }; /* Custom RPC structure. */ Index: src/driver.h =================================================================== RCS file: /data/cvs/libvirt/src/driver.h,v retrieving revision 1.49 diff -u -p -r1.49 driver.h --- src/driver.h 5 Jun 2008 21:12:27 -0000 1.49 +++ src/driver.h 5 Jun 2008 21:26:30 -0000 @@ -234,6 +234,13 @@ typedef int unsigned int flags); typedef int + (*virDrvDomainMemoryPeek) + (virDomainPtr domain, + unsigned long long start, size_t size, + void *buffer, + unsigned int flags); + +typedef int (*virDrvDomainMigratePrepare) (virConnectPtr dconn, char **cookie, @@ -346,6 +353,7 @@ struct _virDriver { virDrvDomainBlockStats domainBlockStats; virDrvDomainInterfaceStats domainInterfaceStats; virDrvDomainBlockPeek domainBlockPeek; + virDrvDomainMemoryPeek domainMemoryPeek; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory getFreeMemory; }; Index: src/libvirt.c =================================================================== RCS file: /data/cvs/libvirt/src/libvirt.c,v retrieving revision 1.145 diff -u -p -r1.145 libvirt.c --- src/libvirt.c 5 Jun 2008 21:12:27 -0000 1.145 +++ src/libvirt.c 5 Jun 2008 21:26:33 -0000 @@ -2666,6 +2666,92 @@ virDomainBlockPeek (virDomainPtr dom, return -1; } +/** + * virDomainMemoryPeek: + * @dom: pointer to the domain object + * @start: start of memory to peek + * @size: size of memory to peek + * @buffer: return buffer (must be at least size bytes) + * @flags: flags, see below + * + * This function allows you to read the contents of a domain's + * memory. + * + * The memory which is read is controlled by the 'start', 'size' + * and 'flags' parameters. + * + * If 'flags' is VIR_MEMORY_VIRTUAL then the 'start' and 'size' + * parameters are interpreted as virtual memory addresses for + * whichever task happens to be running on the domain at the + * moment. Although this sounds haphazard it is in fact what + * you want in order to read Linux kernel state, because it + * ensures that pointers in the kernel image can be interpreted + * coherently. + * + * 'buffer' is the return buffer and must be at least 'size' bytes. + * 'size' may be 0 to test if the call would succeed. + * + * Returns: 0 in case of success or -1 in case of failure. + */ +int +virDomainMemoryPeek (virDomainPtr dom, + unsigned long long start /* really 64 bits */, + size_t size, + void *buffer, + unsigned int flags) +{ + virConnectPtr conn; + DEBUG ("domain=%p, start=%lld, size=%zi, buffer=%p, flags=%d", + dom, start, size, buffer, flags); + + if (!VIR_IS_CONNECTED_DOMAIN (dom)) { + virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + conn = dom->conn; + + /* Flags must be VIR_MEMORY_VIRTUAL at the moment. + * + * Note on access to physical memory: A VIR_MEMORY_PHYSICAL flag is + * a possibility. However it isn't really useful unless the caller + * can also access registers, particularly CR3 on x86 in order to + * get the Page Table Directory. Since registers are different on + * every architecture, that would imply another call to get the + * machine registers. + * + * The QEMU driver handles only VIR_MEMORY_VIRTUAL, mapping it + * to the qemu 'memsave' command which does the virtual to physical + * mapping inside qemu. + * + * At time of writing there is no Xen driver. However the Xen + * hypervisor only lets you map physical pages from other domains, + * and so the Xen driver would have to do the virtual to physical + * mapping by chasing 2, 3 or 4-level page tables from the PTD. + * There is example code in libxc (xc_translate_foreign_address) + * which does this, although we cannot copy this code directly + * because of incompatible licensing. + */ + if (flags != VIR_MEMORY_VIRTUAL) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("flags parameter must be VIR_MEMORY_VIRTUAL")); + return -1; + } + + /* Allow size == 0 as an access test. */ + if (size > 0 && !buffer) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + _("buffer is NULL but size is non-zero")); + return -1; + } + + if (conn->driver->domainMemoryPeek) + return conn->driver->domainMemoryPeek (dom, start, size, + buffer, flags); + + virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + /************************************************************************ * * Index: src/libvirt_sym.version =================================================================== RCS file: /data/cvs/libvirt/src/libvirt_sym.version,v retrieving revision 1.41 diff -u -p -r1.41 libvirt_sym.version --- src/libvirt_sym.version 5 Jun 2008 13:17:45 -0000 1.41 +++ src/libvirt_sym.version 5 Jun 2008 21:26:33 -0000 @@ -74,6 +74,7 @@ virDomainBlockStats; virDomainInterfaceStats; virDomainBlockPeek; + virDomainMemoryPeek; virDomainAttachDevice; virDomainDetachDevice; Index: src/qemu_driver.c =================================================================== RCS file: /data/cvs/libvirt/src/qemu_driver.c,v retrieving revision 1.84 diff -u -p -r1.84 qemu_driver.c --- src/qemu_driver.c 5 Jun 2008 21:12:27 -0000 1.84 +++ src/qemu_driver.c 5 Jun 2008 21:26:35 -0000 @@ -3221,6 +3221,74 @@ found: return ret; } +static int +qemudDomainMemoryPeek (virDomainPtr dom, + unsigned long long offset, size_t size, + void *buffer, + unsigned int flags) +{ + struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData; + struct qemud_vm *vm = qemudFindVMByID (driver, dom->id); + char cmd[256], *info; + char tmp[] = "/tmp/qemumemXXXXXX"; + int fd = -1, ret = -1; + + if (flags != VIR_MEMORY_VIRTUAL) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG, + _("QEMU driver only supports virtual memory addrs")); + return -1; + } + + if (!vm) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, + _("no domain with matching id %d"), dom->id); + return -1; + } + + if (!qemudIsActiveVM(vm)) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("domain is not running")); + return -1; + } + + /* Create a temporary filename. */ + if (mkstemp (tmp) == -1) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, + "%s", strerror (errno)); + return -1; + } + + /* Issue the memsave command. */ + snprintf (cmd, sizeof cmd, "memsave %llu %zi \"%s\"", offset, size, tmp); + if (qemudMonitorCommand (driver, vm, cmd, &info) < 0) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("'info blockstats' command failed")); + goto done; + } + + DEBUG ("memsave reply: %s", info); + free (info); + + /* Read the memory file into buffer. */ + fd = open (tmp, O_RDONLY); + if (fd == -1) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, + "%s", strerror (errno)); + goto done; + } + if (saferead (fd, buffer, size) == (ssize_t) -1) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR, + "%s", strerror (errno)); + goto done; + } + + ret = 0; +done: + if (fd >= 0) close (fd); + unlink (tmp); + return ret; +} + static virNetworkPtr qemudNetworkLookupByUUID(virConnectPtr conn ATTRIBUTE_UNUSED, const unsigned char *uuid) { struct qemud_driver *driver = (struct qemud_driver *)conn->networkPrivateData; @@ -3580,6 +3648,7 @@ static virDriver qemuDriver = { qemudDomainBlockStats, /* domainBlockStats */ qemudDomainInterfaceStats, /* domainInterfaceStats */ qemudDomainBlockPeek, /* domainBlockPeek */ + qemudDomainMemoryPeek, /* domainMemoryPeek */ #if HAVE_NUMACTL qemudNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ qemudNodeGetFreeMemory, /* getFreeMemory */ Index: src/remote_internal.c =================================================================== RCS file: /data/cvs/libvirt/src/remote_internal.c,v retrieving revision 1.77 diff -u -p -r1.77 remote_internal.c --- src/remote_internal.c 5 Jun 2008 21:12:27 -0000 1.77 +++ src/remote_internal.c 5 Jun 2008 21:26:39 -0000 @@ -2420,6 +2420,50 @@ remoteDomainBlockPeek (virDomainPtr doma return 0; } +static int +remoteDomainMemoryPeek (virDomainPtr domain, + unsigned long long offset, + size_t size, + void *buffer, + unsigned int flags) +{ + remote_domain_memory_peek_args args; + remote_domain_memory_peek_ret ret; + GET_PRIVATE (domain->conn, -1); + + if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) { + errorf (domain->conn, VIR_ERR_RPC, + _("memory peek request too large for remote protocol, %zi > %d"), + size, REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX); + return -1; + } + + make_nonnull_domain (&args.dom, domain); + args.offset = offset; + args.size = size; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_MEMORY_PEEK, + (xdrproc_t) xdr_remote_domain_memory_peek_args, + (char *) &args, + (xdrproc_t) xdr_remote_domain_memory_peek_ret, + (char *) &ret) == -1) + return -1; + + if (ret.buffer.buffer_len != size) { + errorf (domain->conn, VIR_ERR_RPC, + _("returned buffer is not same size as requested")); + free (ret.buffer.buffer_val); + return -1; + } + + memcpy (buffer, ret.buffer.buffer_val, size); + free (ret.buffer.buffer_val); + + return 0; +} + /*----------------------------------------------------------------------*/ static int @@ -4831,6 +4875,7 @@ static virDriver driver = { .domainBlockStats = remoteDomainBlockStats, .domainInterfaceStats = remoteDomainInterfaceStats, .domainBlockPeek = remoteDomainBlockPeek, + .domainMemoryPeek = remoteDomainMemoryPeek, .nodeGetCellsFreeMemory = remoteNodeGetCellsFreeMemory, .getFreeMemory = remoteNodeGetFreeMemory, }; Index: src/test.c =================================================================== RCS file: /data/cvs/libvirt/src/test.c,v retrieving revision 1.76 diff -u -p -r1.76 test.c --- src/test.c 5 Jun 2008 13:17:45 -0000 1.76 +++ src/test.c 5 Jun 2008 21:26:40 -0000 @@ -2061,6 +2061,7 @@ static virDriver testDriver = { NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ NULL, /* domainBlockPeek */ + NULL, /* domainMemoryPeek */ testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ };
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list