diff --git a/monitor.c b/monitor.c
index f5f4d0e..0c891cc 100644
--- a/monitor.c
+++ b/monitor.c
@@ -83,6 +83,13 @@ struct mon_fd_t {
     LIST_ENTRY(mon_fd_t) next;
 };

+/* Callback for device info command */
+struct MonDevInfoEntry {
+    const struct MonDevInfo *dev_info;
+    void *dev_opaque;
+    LIST_ENTRY(MonDevInfoEntry) next;
+};
+
 struct Monitor {
     CharDriverState *chr;
     int flags;
@@ -94,6 +101,7 @@ struct Monitor {
     BlockDriverCompletionFunc *password_completion_cb;
     void *password_opaque;
     LIST_HEAD(,mon_fd_t) fds;
+    LIST_HEAD(,MonDevInfoEntry) dev_infos;
     LIST_ENTRY(Monitor) entry;
 };

@@ -1800,6 +1808,19 @@ int monitor_get_fd(Monitor *mon, const char *fdname)
     return -1;
 }

+static void do_info_device(Monitor *mon, const QDict *qdict)
+{
+    struct MonDevInfoEntry *entry;
+    const char *name = qdict_get_str(qdict, "devname");
+
+    LIST_FOREACH(entry, &mon->dev_infos, next) {
+        if (strcmp(entry->dev_info->dev_name, name) != 0) {
+            continue;
+        }
+        entry->dev_info->dev_info_cb(mon, entry->dev_opaque);
+    }
+}
+
 static const mon_cmd_t mon_cmds[] = {
 #include "qemu-monitor.h"
     { NULL, NULL, },
@@ -3045,6 +3066,13 @@ static void monitor_find_completion(const char *cmdline)
                 for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
                     cmd_completion(str, cmd->name);
                 }
+            } else if (!strcmp(cmd->name, "dev_info")) {
+                struct MonDevInfoEntry *entry;
+
+                readline_set_completion_index(cur_mon->rs, strlen(str));
+                LIST_FOREACH(entry, &cur_mon->dev_infos, next) {
+                    cmd_completion(str, entry->dev_info->dev_name);
+                }
             }
             break;
         default:
@@ -3209,6 +3237,17 @@ void monitor_read_bdrv_key_start(Monitor *mon,
BlockDriverState *bs,
         completion_cb(opaque, err);
 }

+void monitor_register_device_info(const struct MonDevInfo *dev_info,
+                                  void *dev_opaque)
+{
+    struct MonDevInfoEntry *entry;
+
+    entry = qemu_malloc(sizeof(*entry));
+    entry->dev_info = dev_info;
+    entry->dev_opaque = dev_opaque;
+    LIST_INSERT_HEAD(&cur_mon->dev_infos, entry, next);
+}
+
 typedef struct QemuErrorSink QemuErrorSink;
 struct QemuErrorSink {
     enum {
diff --git a/monitor.h b/monitor.h
index c7d2d0b..3bcb494 100644
--- a/monitor.h
+++ b/monitor.h
@@ -21,6 +21,16 @@ void monitor_read_bdrv_key_start(Monitor *mon,
BlockDriverState *bs,
                                  BlockDriverCompletionFunc *completion_cb,
                                  void *opaque);

+typedef void DeviceInfoFunc(Monitor *mon, void *opaque);
+
+struct MonDevInfo {
+    const char *dev_name;
+    DeviceInfoFunc *dev_info_cb;
+};
+
+void monitor_register_device_info(const struct MonDevInfo *dev_info,
+                                  void *dev_opaque);
+
 int monitor_get_fd(Monitor *mon, const char *fdname);

 void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap);
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index 9f91873..af01245 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -645,6 +645,13 @@ Close the file descriptor previously assigned to
@var{fdname} using the
 used by another monitor command.
 ETEXI

+    { "dev_info", "devname:s", do_info_device,
+      "device name", "show device information" },
+STEXI
+@item device @var{devicename}
+Show information about a device.
+ETEXI
+
 STEXI
 @end table
 ETEXI
diff --git a/vl.c b/vl.c
index 098daaa..6bb4084 100644
--- a/vl.c
+++ b/vl.c
@@ -5908,6 +5908,15 @@ int main(int argc, char **argv, char **envp)
             }
         }
     }
+    qemu_chr_initial_reset();
+
+    for (i = 0; i < MAX_MONITOR_DEVICES; i++) {
+        if (monitor_devices[i] && monitor_hds[i]) {
+            monitor_init(monitor_hds[i],
+                         MONITOR_USE_READLINE |
+                         ((i == 0) ? MONITOR_IS_DEFAULT : 0));
+        }
+    }

     for(i = 0; i < MAX_SERIAL_PORTS; i++) {
         const char *devname = serial_devices[i];
@@ -6052,15 +6061,6 @@ int main(int argc, char **argv, char **envp)
     }

     text_consoles_set_display(display_state);
-    qemu_chr_initial_reset();
-
-    for (i = 0; i < MAX_MONITOR_DEVICES; i++) {
-        if (monitor_devices[i] && monitor_hds[i]) {
-            monitor_init(monitor_hds[i],
-                         MONITOR_USE_READLINE |
-                         ((i == 0) ? MONITOR_IS_DEFAULT : 0));
-        }
-    }

     for(i = 0; i < MAX_SERIAL_PORTS; i++) {
         const char *devname = serial_devices[i];
