diff mbox

[RFC,6/7] hw/arm/virt: Add cpu hotplug support

Message ID 1424167806-8372-7-git-send-email-zhaoshenglong@huawei.com
State New
Headers show

Commit Message

Shannon Zhao Feb. 17, 2015, 10:10 a.m. UTC
Add a hotplug device in machine virt and add cpu hotplug support
using cpu-add.

Signed-off-by: Shannon Zhao <zhaoshenglong@huawei.com>
---
 hw/arm/virt.c                  |  159 +++++++++++++++++++++++++++++++++++++++-
 include/hw/acpi/virt-hotplug.h |    1 +
 2 files changed, 159 insertions(+), 1 deletions(-)
diff mbox

Patch

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index f689bc3..760afbb 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -43,6 +43,8 @@ 
 #include "qemu/bitops.h"
 #include "qemu/error-report.h"
 #include "hw/arm/virt-acpi-build.h"
+#include "hw/acpi/virt-hotplug.h"
+#include "hw/hotplug.h"
 
 #define NUM_VIRTIO_TRANSPORTS 32
 
@@ -76,6 +78,7 @@  enum {
     VIRT_RTC,
     VIRT_FW_CFG,
     VIRT_GPIO,
+    VIRT_CPU_HOTPLUG,
 };
 
 typedef struct MemMapEntry {
@@ -102,8 +105,11 @@  typedef struct {
 typedef struct {
     MachineState parent;
     bool secure;
+    HotplugHandler *acpi_dev;
 } VirtMachineState;
 
+#define VIRT_MACHINE_ACPI_DEVICE_PROP "acpi-device"
+
 #define TYPE_VIRT_MACHINE   "virt"
 #define VIRT_MACHINE(obj) \
     OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE)
@@ -135,6 +141,7 @@  static const MemMapEntry a15memmap[] = {
     [VIRT_RTC] =        { 0x09010000, 0x00001000 },
     [VIRT_FW_CFG] =     { 0x09020000, 0x0000000a },
     [VIRT_GPIO] =       { 0x09500000, 0x00001000 },
+    [VIRT_CPU_HOTPLUG] =       { 0x09600000, 0x00000020 },
     [VIRT_MMIO] =       { 0x0a000000, 0x00000200 },
     /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
     /* 0x10000000 .. 0x40000000 reserved for PCI */
@@ -381,7 +388,7 @@  static void create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
 
     gicdev = qdev_create(NULL, gictype);
     qdev_prop_set_uint32(gicdev, "revision", 2);
-    qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus);
+    qdev_prop_set_uint32(gicdev, "num-cpu", max_cpus);
     /* Note that the num-irq property counts both internal and external
      * interrupts; there are always 32 of the former (mandated by GIC spec).
      */
@@ -630,6 +637,136 @@  void virt_guest_info_machine_done(Notifier *notifier, void *data)
     virt_acpi_setup(&guest_info_state->info);
 }
 
+static void virt_new_cpu(const char *cpu_model, int64_t apic_id,
+                          Error **errp)
+{
+    ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
+    Object *cpuobj;
+    VirtBoardInfo *vbi;
+    SysBusDevice *gicbusdev;
+
+    vbi = find_machine_info(cpu_model);
+
+    if (!oc) {
+        fprintf(stderr, "Unable to find CPU definition\n");
+        exit(1);
+    }
+    cpuobj = object_new(object_class_get_name(oc));
+
+    object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, "psci-conduit",
+                            NULL);
+
+    /* Secondary CPUs start in PSCI powered-down state */
+    object_property_set_bool(cpuobj, true, "start-powered-off", NULL);
+
+    if (object_property_find(cpuobj, "reset-cbar", NULL)) {
+        object_property_set_int(cpuobj, vbi->memmap[VIRT_CPUPERIPHS].base,
+                            "reset-cbar", &error_abort);
+    }
+
+    object_property_set_bool(cpuobj, true, "realized", NULL);
+
+    const char *gictype = "arm_gic";
+    if (kvm_irqchip_in_kernel()) {
+        gictype = "kvm-arm-gic";
+    }
+
+    bool ambig;
+    Object *o = object_resolve_path_type("", gictype, &ambig);
+    DeviceState *gicdev = DEVICE(o);
+    DeviceState *cpudev = DEVICE(cpuobj);
+    gicbusdev = SYS_BUS_DEVICE(gicdev);
+    int ppibase = NUM_IRQS + apic_id * 32;
+    /* physical timer; we wire it up to the non-secure timer's ID,
+     * since a real A15 always has TrustZone but QEMU doesn't.
+     */
+    qdev_connect_gpio_out(cpudev, 0,
+                          qdev_get_gpio_in(gicdev, ppibase + 30));
+    /* virtual timer */
+    qdev_connect_gpio_out(cpudev, 1,
+                          qdev_get_gpio_in(gicdev, ppibase + 27));
+
+    sysbus_connect_irq(gicbusdev, apic_id,
+                       qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+}
+
+static const char *current_cpu_model;
+
+static void virt_hot_add_cpu(const int64_t id, Error **errp)
+{
+    int64_t apic_id = arm_cpu_apic_id_from_index(id);
+    if (id < 0) {
+        error_setg(errp, "Invalid CPU id: %" PRIi64, id);
+        return;
+    }
+
+    if (cpu_exists(apic_id)) {
+        error_setg(errp, "Unable to add CPU: %" PRIi64
+                   ", it already exists", id);
+        return;
+    }
+
+    if (id >= max_cpus) {
+        error_setg(errp, "Unable to add CPU: %" PRIi64
+                   ", max allowed: %d", id, max_cpus - 1);
+        return;
+    }
+
+    if (apic_id >= VIRT_ACPI_CPU_HOTPLUG_ID_LIMIT) {
+        error_setg(errp, "Unable to add CPU: %" PRIi64
+                   ", resulting APIC ID (%" PRIi64 ") is too large",
+                   id, apic_id);
+        return;
+    }
+
+    virt_new_cpu(current_cpu_model, apic_id, errp);
+}
+
+static void virt_cpu_plug(HotplugHandler *hotplug_dev,
+                        DeviceState *dev, Error **errp)
+{
+    HotplugHandlerClass *hhc;
+    Error *local_err = NULL;
+    VirtMachineState *virtms = VIRT_MACHINE(hotplug_dev);
+
+    if (!dev->hotplugged) {
+        goto out;
+    }
+
+    if (!virtms->acpi_dev) {
+        error_setg(&local_err,
+                   "cpu hotplug is not enabled: missing acpi device");
+        goto out;
+    }
+
+    hhc = HOTPLUG_HANDLER_GET_CLASS(virtms->acpi_dev);
+    hhc->plug(HOTPLUG_HANDLER(virtms->acpi_dev), dev, &local_err);
+    if (local_err) {
+        goto out;
+    }
+
+out:
+    error_propagate(errp, local_err);
+}
+
+static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
+                                      DeviceState *dev, Error **errp)
+{
+    if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+        virt_cpu_plug(hotplug_dev, dev, errp);
+    }
+}
+
+static HotplugHandler *virt_get_hotpug_handler(MachineState *machine,
+                                             DeviceState *dev)
+{
+    if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+        return HOTPLUG_HANDLER(machine);
+    }
+
+    return NULL;
+}
+
 static void machvirt_init(MachineState *machine)
 {
     VirtMachineState *vms = VIRT_MACHINE(machine);
@@ -641,10 +778,12 @@  static void machvirt_init(MachineState *machine)
     VirtBoardInfo *vbi;
     VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
     VirtGuestInfo *guest_info = &guest_info_state->info;
+    DeviceState *virt_hotplug;
 
     if (!cpu_model) {
         cpu_model = "cortex-a15";
     }
+    current_cpu_model = cpu_model;
 
     vbi = find_machine_info(cpu_model);
 
@@ -710,6 +849,16 @@  static void machvirt_init(MachineState *machine)
 
     create_gpio(vbi, pic);
 
+    virt_hotplug_init(&virt_hotplug);
+
+    object_property_add_link(OBJECT(machine), VIRT_MACHINE_ACPI_DEVICE_PROP,
+                             TYPE_HOTPLUG_HANDLER,
+                             (Object **)&vms->acpi_dev,
+                             object_property_allow_set_link,
+                             OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
+    object_property_set_link(OBJECT(machine), OBJECT(virt_hotplug),
+                            VIRT_MACHINE_ACPI_DEVICE_PROP, &error_abort);
+
     /* Create mmio transports, so the user can create virtio backends
      * (which will be automatically plugged in to the transports). If
      * no backend is created the transport will just sit harmlessly idle.
@@ -772,11 +921,15 @@  static void virt_instance_init(Object *obj)
 static void virt_class_init(ObjectClass *oc, void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
+    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
 
     mc->name = TYPE_VIRT_MACHINE;
     mc->desc = "ARM Virtual Machine",
     mc->init = machvirt_init;
+    mc->hot_add_cpu = virt_hot_add_cpu,
     mc->max_cpus = 8;
+    mc->get_hotplug_handler = virt_get_hotpug_handler;
+    hc->plug = virt_machine_device_plug_cb;
 }
 
 static const TypeInfo machvirt_info = {
@@ -786,6 +939,10 @@  static const TypeInfo machvirt_info = {
     .instance_init = virt_instance_init,
     .class_size = sizeof(VirtMachineClass),
     .class_init = virt_class_init,
+    .interfaces = (InterfaceInfo[]) {
+         { TYPE_HOTPLUG_HANDLER },
+         { }
+    },
 };
 
 static void machvirt_machine_init(void)
diff --git a/include/hw/acpi/virt-hotplug.h b/include/hw/acpi/virt-hotplug.h
index a668d16..8f94235 100644
--- a/include/hw/acpi/virt-hotplug.h
+++ b/include/hw/acpi/virt-hotplug.h
@@ -3,6 +3,7 @@ 
 
 #include "qemu/typedefs.h"
 
+#define VIRT_ACPI_CPU_HOTPLUG_ID_LIMIT 256
 #define VIRT_CPU_HOTPLUG_MMIO_BASE 0x09600000
 
 void virt_hotplug_init(DeviceState **virt_hotplug);