qdev hotplug: infrastructure and monitor commands.

Adds device_add and device_del commands.  device_add accepts accepts
the same syntax like the -device command line switch.  device_del
expects a device id.  So you should tag your devices with ids if you
want to remove them later on, like this:

  device_add pci-ohci,id=ohci
  device_del ohci

Unplugging via pci_del or usb_del works too.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/hw/qdev.c b/hw/qdev.c
index 064389d..ebddcae 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -31,6 +31,8 @@
 #include "monitor.h"
 
 /* This is a nasty hack to allow passing a NULL bus to qdev_create.  */
+static int qdev_hotplug = 0;
+
 static BusState *main_system_bus;
 
 static DeviceInfo *device_info_list;
@@ -102,6 +104,10 @@
     qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
     qdev_prop_set_compat(dev);
     QLIST_INSERT_HEAD(&bus->children, dev, sibling);
+    if (qdev_hotplug) {
+        assert(bus->allow_hotplug);
+        dev->hotplugged = 1;
+    }
     dev->state = DEV_STATE_CREATED;
     return dev;
 }
@@ -192,6 +198,11 @@
                    path ? path : info->bus_info->name, info->name);
         return NULL;
     }
+    if (qdev_hotplug && !bus->allow_hotplug) {
+        qemu_error("Bus %s does not support hotplugging\n",
+                   bus->name);
+        return NULL;
+    }
 
     /* create device, set properties */
     qdev = qdev_create(bus, driver);
@@ -229,6 +240,24 @@
     return 0;
 }
 
+int qdev_unplug(DeviceState *dev)
+{
+    if (!dev->parent_bus->allow_hotplug) {
+        qemu_error("Bus %s does not support hotplugging\n",
+                   dev->parent_bus->name);
+        return -1;
+    }
+    return dev->info->unplug(dev);
+}
+
+/* can be used as ->unplug() callback for the simple cases */
+int qdev_simple_unplug_cb(DeviceState *dev)
+{
+    /* just zap it */
+    qdev_free(dev);
+    return 0;
+}
+
 /* Unlink device from bus and free the structure.  */
 void qdev_free(DeviceState *dev)
 {
@@ -252,6 +281,15 @@
     qemu_free(dev);
 }
 
+void qdev_machine_creation_done(void)
+{
+    /*
+     * ok, initial machine setup is done, starting from now we can
+     * only create hotpluggable devices
+     */
+    qdev_hotplug = 1;
+}
+
 /* Get a character (serial) device interface.  */
 CharDriverState *qdev_init_chardev(DeviceState *dev)
 {
@@ -370,6 +408,24 @@
     return NULL;
 }
 
+static DeviceState *qdev_find_recursive(BusState *bus, const char *id)
+{
+    DeviceState *dev, *ret;
+    BusState *child;
+
+    QLIST_FOREACH(dev, &bus->children, sibling) {
+        if (dev->id && strcmp(dev->id, id) == 0)
+            return dev;
+        QLIST_FOREACH(child, &dev->child_bus, sibling) {
+            ret = qdev_find_recursive(child, id);
+            if (ret) {
+                return ret;
+            }
+        }
+    }
+    return NULL;
+}
+
 static void qbus_list_bus(DeviceState *dev, char *dest, int len)
 {
     BusState *child;
@@ -647,3 +703,26 @@
         monitor_printf(mon, "%s\n", msg);
     }
 }
+
+void do_device_add(Monitor *mon, const QDict *qdict)
+{
+    QemuOpts *opts;
+
+    opts = qemu_opts_parse(&qemu_device_opts,
+                           qdict_get_str(qdict, "config"), "driver");
+    if (opts)
+        qdev_device_add(opts);
+}
+
+void do_device_del(Monitor *mon, const QDict *qdict)
+{
+    const char *id = qdict_get_str(qdict, "id");
+    DeviceState *dev;
+
+    dev = qdev_find_recursive(main_system_bus, id);
+    if (NULL == dev) {
+        qemu_error("Device '%s' not found\n", id);
+        return;
+    }
+    qdev_unplug(dev);
+}