Author: jhb
Date: Fri May 24 00:34:13 2019
New Revision: 348212
URL: https://svnweb.freebsd.org/changeset/base/348212

Log:
  Add support for writing to guest memory in the debug server.
  
  - Add a write_mem counterpart to read_mem to handle writes to MMIO.
  - Add support for the GDB 'M' packet to write bytes to the guest's
    memory.  For MMIO writes, attempt to batch writes up into words.
    This is imprecise, but if you write a single 2 or 4-byte aligned
    word, it should be treated as a single MMIO write operation.
  - While here, tidy up the parsing of the 'm' command used for reading
    memory to match 'M'.
  
  Reviewed by:  markj, Scott Phillips <d.scott.phill...@intel.com>
  MFC after:    2 weeks
  Differential Revision:        https://reviews.freebsd.org/D20307

Modified:
  head/usr.sbin/bhyve/gdb.c
  head/usr.sbin/bhyve/mem.c
  head/usr.sbin/bhyve/mem.h

Modified: head/usr.sbin/bhyve/gdb.c
==============================================================================
--- head/usr.sbin/bhyve/gdb.c   Fri May 24 00:00:29 2019        (r348211)
+++ head/usr.sbin/bhyve/gdb.c   Fri May 24 00:34:13 2019        (r348212)
@@ -767,15 +767,24 @@ gdb_read_mem(const uint8_t *data, size_t len)
        bool started;
        int error;
 
+       /* Skip 'm' */
+       data += 1;
+       len -= 1;
+
+       /* Parse and consume address. */
        cp = memchr(data, ',', len);
-       if (cp == NULL) {
+       if (cp == NULL || cp == data) {
                send_error(EINVAL);
                return;
        }
-       gva = parse_integer(data + 1, cp - (data + 1));
-       resid = parse_integer(cp + 1, len - (cp + 1 - data));
-       started = false;
+       gva = parse_integer(data, cp - data);
+       len -= (cp - data) + 1;
+       data += (cp - data) + 1;
 
+       /* Parse length. */
+       resid = parse_integer(data, len);
+
+       started = false;
        while (resid > 0) {
                error = guest_vaddr2paddr(cur_vcpu, gva, &gpa);
                if (error == -1) {
@@ -861,6 +870,119 @@ gdb_read_mem(const uint8_t *data, size_t len)
        finish_packet();
 }
 
+static void
+gdb_write_mem(const uint8_t *data, size_t len)
+{
+       uint64_t gpa, gva, val;
+       uint8_t *cp;
+       size_t resid, todo, bytes;
+       int error;
+
+       /* Skip 'M' */
+       data += 1;
+       len -= 1;
+
+       /* Parse and consume address. */
+       cp = memchr(data, ',', len);
+       if (cp == NULL || cp == data) {
+               send_error(EINVAL);
+               return;
+       }
+       gva = parse_integer(data, cp - data);
+       len -= (cp - data) + 1;
+       data += (cp - data) + 1;
+
+       /* Parse and consume length. */
+       cp = memchr(data, ':', len);
+       if (cp == NULL || cp == data) {
+               send_error(EINVAL);
+               return;
+       }
+       resid = parse_integer(data, cp - data);
+       len -= (cp - data) + 1;
+       data += (cp - data) + 1;
+
+       /* Verify the available bytes match the length. */
+       if (len != resid * 2) {
+               send_error(EINVAL);
+               return;
+       }
+
+       while (resid > 0) {
+               error = guest_vaddr2paddr(cur_vcpu, gva, &gpa);
+               if (error == -1) {
+                       send_error(errno);
+                       return;
+               }
+               if (error == 0) {
+                       send_error(EFAULT);
+                       return;
+               }
+
+               /* Write bytes to current page. */
+               todo = getpagesize() - gpa % getpagesize();
+               if (todo > resid)
+                       todo = resid;
+
+               cp = paddr_guest2host(ctx, gpa, todo);
+               if (cp != NULL) {
+                       /*
+                        * If this page is guest RAM, write it a byte
+                        * at a time.
+                        */
+                       while (todo > 0) {
+                               assert(len >= 2);
+                               *cp = parse_byte(data);
+                               data += 2;
+                               len -= 2;
+                               cp++;
+                               gpa++;
+                               gva++;
+                               resid--;
+                               todo--;
+                       }
+               } else {
+                       /*
+                        * If this page isn't guest RAM, try to handle
+                        * it via MMIO.  For MMIO requests, use
+                        * aligned writes of words when possible.
+                        */
+                       while (todo > 0) {
+                               if (gpa & 1 || todo == 1) {
+                                       bytes = 1;
+                                       val = parse_byte(data);
+                               } else if (gpa & 2 || todo == 2) {
+                                       bytes = 2;
+                                       val = parse_byte(data) |
+                                           (parse_byte(data + 2) << 8);
+                               } else {
+                                       bytes = 4;
+                                       val = parse_byte(data) |
+                                           (parse_byte(data + 2) << 8) |
+                                           (parse_byte(data + 4) << 16) |
+                                           (parse_byte(data + 6) << 24);
+                               }
+                               error = write_mem(ctx, cur_vcpu, gpa, val,
+                                   bytes);
+                               if (error == 0) {
+                                       gpa += bytes;
+                                       gva += bytes;
+                                       resid -= bytes;
+                                       todo -= bytes;
+                                       data += 2 * bytes;
+                                       len -= 2 * bytes;
+                               } else {
+                                       send_error(EFAULT);
+                                       return;
+                               }
+                       }
+               }
+               assert(resid == 0 || gpa % getpagesize() == 0);
+       }
+       assert(len == 0);
+       send_ok();
+}
+
 static bool
 command_equals(const uint8_t *data, size_t len, const char *cmd)
 {
@@ -1000,6 +1122,9 @@ handle_command(const uint8_t *data, size_t len)
        case 'm':
                gdb_read_mem(data, len);
                break;
+       case 'M':
+               gdb_write_mem(data, len);
+               break;
        case 'T': {
                int tid;
 
@@ -1035,7 +1160,6 @@ handle_command(const uint8_t *data, size_t len)
                finish_packet();
                break;
        case 'G': /* TODO */
-       case 'M': /* TODO */
        case 'v':
                /* Handle 'vCont' */
                /* 'vCtrlC' */

Modified: head/usr.sbin/bhyve/mem.c
==============================================================================
--- head/usr.sbin/bhyve/mem.c   Fri May 24 00:00:29 2019        (r348211)
+++ head/usr.sbin/bhyve/mem.c   Fri May 24 00:34:13 2019        (r348212)
@@ -251,30 +251,43 @@ emulate_mem(struct vmctx *ctx, int vcpu, uint64_t padd
        return (access_memory(ctx, vcpu, paddr, emulate_mem_cb, &ema));
 }
 
-struct read_mem_args {
-       uint64_t *rval;
+struct rw_mem_args {
+       uint64_t *val;
        int size;
+       int operation;
 };
 
 static int
-read_mem_cb(struct vmctx *ctx, int vcpu, uint64_t paddr, struct mem_range *mr,
+rw_mem_cb(struct vmctx *ctx, int vcpu, uint64_t paddr, struct mem_range *mr,
     void *arg)
 {
-       struct read_mem_args *rma;
+       struct rw_mem_args *rma;
 
        rma = arg;
-       return (mr->handler(ctx, vcpu, MEM_F_READ, paddr, rma->size,
-           rma->rval, mr->arg1, mr->arg2));
+       return (mr->handler(ctx, vcpu, rma->operation, paddr, rma->size,
+           rma->val, mr->arg1, mr->arg2));
 }
 
 int
 read_mem(struct vmctx *ctx, int vcpu, uint64_t gpa, uint64_t *rval, int size)
 {
-       struct read_mem_args rma;
+       struct rw_mem_args rma;
 
-       rma.rval = rval;
+       rma.val = rval;
        rma.size = size;
-       return (access_memory(ctx, vcpu, gpa, read_mem_cb, &rma));
+       rma.operation = MEM_F_READ;
+       return (access_memory(ctx, vcpu, gpa, rw_mem_cb, &rma));
+}
+
+int
+write_mem(struct vmctx *ctx, int vcpu, uint64_t gpa, uint64_t wval, int size)
+{
+       struct rw_mem_args rma;
+
+       rma.val = &wval;
+       rma.size = size;
+       rma.operation = MEM_F_WRITE;
+       return (access_memory(ctx, vcpu, gpa, rw_mem_cb, &rma));
 }
 
 static int

Modified: head/usr.sbin/bhyve/mem.h
==============================================================================
--- head/usr.sbin/bhyve/mem.h   Fri May 24 00:00:29 2019        (r348211)
+++ head/usr.sbin/bhyve/mem.h   Fri May 24 00:34:13 2019        (r348212)
@@ -61,5 +61,7 @@ int   read_mem(struct vmctx *ctx, int vcpu, uint64_t gpa
 int    register_mem(struct mem_range *memp);
 int    register_mem_fallback(struct mem_range *memp);
 int    unregister_mem(struct mem_range *memp);
+int    write_mem(struct vmctx *ctx, int vcpu, uint64_t gpa, uint64_t wval,
+                 int size);
 
 #endif /* _MEM_H_ */
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to