diff --git a/monitor.c b/monitor.c
index 6203f75..401a27a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -711,7 +711,7 @@ static void do_info_version_print(Monitor *mon, const QObject *data)
  *
  * { qemu: { "major": 0, "minor": 11, "micro": 5 }, "package": "" }
  */
-static void do_info_version(Monitor *mon, QObject **ret_data)
+void do_info_version(Monitor *mon, QObject **ret_data)
 {
     const char *version = QEMU_VERSION;
     int major = 0, minor = 0, micro = 0;
@@ -760,7 +760,7 @@ static QObject *get_cmd_dict(const char *name)
     return qobject_from_jsonf("{ 'name': %s }", p);
 }
 
-static void do_info_commands(Monitor *mon, QObject **ret_data)
+void do_info_commands(Monitor *mon, QObject **ret_data)
 {
     QList *cmd_list;
     const mon_cmd_t *cmd;
diff --git a/monitor.h b/monitor.h
index 733485f..dc376af 100644
--- a/monitor.h
+++ b/monitor.h
@@ -57,5 +57,7 @@ typedef void (MonitorCompletion)(void *opaque, QObject *ret_data);
 void monitor_set_error(Monitor *mon, QError *qerror);
 
 void do_info_argv(Monitor *mon, QObject **data);
+void do_info_version(Monitor *mon, QObject **ret_data);
+void do_info_commands(Monitor *mon, QObject **ret_data);
 
 #endif /* !MONITOR_H */
diff --git a/qemu-options.hx b/qemu-options.hx
index a6928b7..5f82347 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -27,6 +27,15 @@ STEXI
 Display version information and exit
 ETEXI
 
+DEF("capabilities", HAS_ARG, QEMU_OPTION_capabilities,
+    "-capabilities   all|version|machines|devices|cputypes|target|commands|argv|netdev\n"
+    "                display capabilities of the QEMU binary and exit\n", QEMU_ARCH_ALL)
+STEXI
+@item -capabilities  all|version|machines|devices|cputypes|target|commands|argv|netdev
+@findex -capabilities
+Display capabilities of the QEMU binary and exit
+ETEXI
+
 DEF("M", HAS_ARG, QEMU_OPTION_M,
     "-M machine      select emulated machine (-M ? for list)\n", QEMU_ARCH_ALL)
 STEXI
diff --git a/vl.c b/vl.c
index de010cc..1f165a1 100644
--- a/vl.c
+++ b/vl.c
@@ -1988,6 +1988,97 @@ static void version(void)
 }
 
 
+enum {
+    QEMU_CAPS_VERSION,
+    QEMU_CAPS_MACHINES,
+    QEMU_CAPS_DEVICES,
+    QEMU_CAPS_CPUTYPES,
+    QEMU_CAPS_TARGET,
+    QEMU_CAPS_COMMANDS,
+    QEMU_CAPS_ARGV,
+    QEMU_CAPS_NETDEV,
+    QEMU_CAPS_CONFIG,
+
+    QEMU_CAPS_LAST
+};
+
+QEMU_ENUM_DECL(qemu_caps_flag);
+QEMU_ENUM_IMPL(qemu_caps_flag, QEMU_CAPS_LAST,
+               "version",
+               "machines",
+               "devices",
+               "cputypes",
+               "target",
+               "commands",
+               "argv",
+               "netdev",
+               "config");
+
+static int qemu_caps_flags_from_string(const char *flagsstr)
+{
+    if (strcmp(flagsstr, "all") == 0)
+        return ~0;
+    else {
+        int i = qemu_caps_flag_from_string(flagsstr);
+        if (i < 0)
+            return 0;
+        return (1 << i);
+    }
+
+    return 0;
+}
+typedef void (*qemu_caps_handler)(Monitor *mon, QObject **);
+
+/* Binding capabilities to a subset of monitor commands.
+ * The commands must only use static binary state, no
+ * VM runtime state and must accept mon=NULL
+ */
+static const qemu_caps_handler qemu_caps_handlers[] = {
+    do_info_version,
+    do_info_machines,
+    do_info_devices,
+    do_info_cpu_types,
+    do_info_target,
+    do_info_commands,
+    do_info_argv,
+    do_info_netdev,
+    do_info_config,
+};
+verify(ARRAY_SIZE(qemu_caps_handlers) == QEMU_CAPS_LAST);
+
+static void qemu_show_caps(const char *flagsstr)
+{
+    QDict *data = qdict_new();
+    QString *json;
+    int i;
+    int flags;
+
+    flags = qemu_caps_flags_from_string(flagsstr);
+    if (flags == 0) {
+        fprintf(stderr, "Unknown capabilities '%s'\n", flagsstr);
+        exit(1);
+    }
+
+    for (i = 0 ; i < QEMU_CAPS_LAST ; i++) {
+        if (flags & (1 << i)) {
+            QObject *val;
+            qemu_caps_handler handler = qemu_caps_handlers[i];
+            const char *name = qemu_caps_flag_to_string(i);
+            (*handler)(NULL, &val);
+            qdict_put_obj(data, name, val);
+        }
+    }
+    json = qobject_to_json_pretty(QOBJECT(data));
+    assert(json != NULL);
+
+    qstring_append_chr(json, '\n');
+    fprintf(stdout, qstring_get_str(json));
+
+    QDECREF(json);
+
+}
+
+
 /**
  * do_info_argv():
  *
@@ -2647,6 +2738,7 @@ int main(int argc, char **argv, char **envp)
 #endif
     int show_vnc_port = 0;
     int defconfig = 1;
+    const char *showcaps = NULL;
 
     error_set_progname(argv[0]);
 
@@ -3020,6 +3112,9 @@ int main(int argc, char **argv, char **envp)
                 version();
                 exit(0);
                 break;
+            case QEMU_OPTION_capabilities:
+                showcaps = optarg;
+                break;
             case QEMU_OPTION_m: {
                 uint64_t value;
                 char *ptr;
@@ -3785,6 +3880,11 @@ int main(int argc, char **argv, char **envp)
     if (qemu_opts_foreach(&qemu_device_opts, device_help_func, NULL, 0) != 0)
         exit(0);
 
+    if (showcaps) {
+        qemu_show_caps(showcaps);
+        exit(0);
+    }
+
     if (watchdog) {
         i = select_watchdog(watchdog);
         if (i > 0)
