Qemu: Enable the vcpu-hotplug for arm

Initially enable vcpu hotplug in qemu for arm base on Salli's work[1].

Fixes:#3280

Signed-off-by: Huang Shijie <shijie8@gmail.com>
[1] https://github.com/salil-mehta/qemu/tree/virt-cpuhp-armv8/rfc-v1
This commit is contained in:
Huang Shijie 2022-01-14 13:26:32 +00:00
parent 97e18cf2d0
commit 2d0ec00aff
29 changed files with 4177 additions and 0 deletions

View File

@ -0,0 +1,79 @@
From cbc35b3747ff8c50e64e3b8aeecf1b782ee27cad Mon Sep 17 00:00:00 2001
From: Huang Shijie <shijie8@gmail.com>
Date: Mon, 22 Nov 2021 17:51:11 +0800
Subject: [PATCH 01/28] arm/cpuhp: Add QMP vcpu params validation support
From Salil Mehta <salil.mehta@huawei.com>
For now, vcpu hotplug is only supported with single socket single thread,
single die. NUMA is not supported either and everthing falls into single
node. Work to properly support these could be taken later once community
agrees with the base framework changes being presented to support ARM vcpu
hotplug in QEMU. Hence, these checks.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 81eda46b0b..99d59fada2 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2564,6 +2564,44 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
return NULL;
}
+static void virt_smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
+{
+ unsigned cpus = config->has_cpus ? config->cpus : 1;
+ unsigned sockets = config->has_sockets ? config->sockets: 1;
+ unsigned cores = config->has_cores ? config->cores : cpus;
+ unsigned threads = config->has_threads ? config->threads: 1;
+ unsigned int max_cpus;
+
+ if (sockets > 1 || threads > 1) {
+ error_report("does not support more than one socket or thread");
+ exit(1);
+ }
+
+ if (cores != cpus) {
+ error_report("cpu topology: "
+ "sockets (%u) * cores (%u) * threads (%u) < "
+ "smp_cpus (%u)",
+ sockets, cores, threads, cpus);
+ exit(1);
+ }
+
+ max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
+ if (sockets * cores * threads > max_cpus) {
+ error_report("cpu topology: "
+ "sockets (%u) * cores (%u) * threads (%u) > "
+ "maxcpus (%u)",
+ sockets, cores, threads,
+ max_cpus);
+ exit(1);
+ }
+
+ ms->smp.max_cpus = max_cpus;
+ ms->smp.sockets = sockets;
+ ms->smp.cpus = cpus;
+ ms->smp.cores = cores;
+ ms->smp.threads = threads;
+}
+
/*
* for arm64 kvm_type [7-0] encodes the requested number of bits
* in the IPA address space
@@ -2641,6 +2679,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->auto_enable_numa_with_memhp = true;
mc->auto_enable_numa_with_memdev = true;
mc->default_ram_id = "mach-virt.ram";
+ mc->smp_parse = virt_smp_parse;
object_class_property_add(oc, "acpi", "OnOffAuto",
virt_get_acpi, virt_set_acpi,
--
2.30.2

View File

@ -0,0 +1,101 @@
From a24ce04b7c2e958a0730f19e6f54e6570a075b20 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Tue, 23 Nov 2021 15:08:45 +0800
Subject: [PATCH 02/28] arm/cpuhp: Add new ARMCPU core-id property
This shall be used to store user specified core index and shall be directly
used as slot-index during hot{plug|unplug} of vcpu.
For now, we are not taking into account of other topology info like thread-id,
socket-id to derive mp-affinity. Host KVM uses vcpu-id to derive the mpidr for
the vcpu of the guest. This is not in exact corroboration with the ARM spec
view of the MPIDR. Hence, the concept of threads or SMT bit present as part of
the MPIDR_EL1 also gets lost.
Also, we need ACPI PPTT Table support in QEMU to be able to export this
topology info to the guest VM and the info should be consistent with what host
cpu supports if accel=kvm is being used.
Perhaps some comments on this will help? @Andrew/drjones@redhat.com
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 5 +++++
target/arm/cpu.c | 5 +++++
target/arm/cpu.h | 1 +
3 files changed, 11 insertions(+)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 99d59fada2..86e1470925 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1944,6 +1944,7 @@ static void machvirt_init(MachineState *machine)
&error_fatal);
aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL);
+ object_property_set_int(cpuobj, "core-id", n, NULL);
if (!vms->secure) {
object_property_set_bool(cpuobj, "has_el3", false, NULL);
@@ -2357,6 +2358,7 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
{
int n;
unsigned int max_cpus = ms->smp.max_cpus;
+ unsigned int smp_threads = ms->smp.threads;
VirtMachineState *vms = VIRT_MACHINE(ms);
if (ms->possible_cpus) {
@@ -2369,10 +2371,13 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
ms->possible_cpus->len = max_cpus;
for (n = 0; n < ms->possible_cpus->len; n++) {
ms->possible_cpus->cpus[n].type = ms->cpu_type;
+ ms->possible_cpus->cpus[n].vcpus_count = smp_threads;
ms->possible_cpus->cpus[n].arch_id =
virt_cpu_mp_affinity(vms, n);
ms->possible_cpus->cpus[n].props.has_thread_id = true;
ms->possible_cpus->cpus[n].props.thread_id = n;
+ ms->possible_cpus->cpus[n].props.has_core_id = true;
+ ms->possible_cpus->cpus[n].props.core_id = n;
}
return ms->possible_cpus;
}
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 2866dd7658..5dc3fa6c3a 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -1130,6 +1130,9 @@ static Property arm_cpu_has_dsp_property =
static Property arm_cpu_has_mpu_property =
DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true);
+static Property arm_cpu_coreid_property =
+ DEFINE_PROP_INT32("core-id", ARMCPU, core_id, -1);
+
/* This is like DEFINE_PROP_UINT32 but it doesn't set the default value,
* because the CPU initfn will have already set cpu->pmsav7_dregion to
* the right value for that particular CPU type, and we don't want
@@ -1303,6 +1306,8 @@ void arm_cpu_post_init(Object *obj)
kvm_arm_add_vcpu_properties(obj);
}
+ qdev_property_add_static(DEVICE(obj), &arm_cpu_coreid_property);
+
#ifndef CONFIG_USER_ONLY
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) &&
cpu_isar_feature(aa64_mte, cpu)) {
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 9f0a5f84d5..ba11468ab5 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -999,6 +999,7 @@ struct ARMCPU {
QLIST_HEAD(, ARMELChangeHook) el_change_hooks;
int32_t node_id; /* NUMA node this CPU belongs to */
+ int32_t core_id; /* core-id of this ARM VCPU */
/* Used to synchronize KVM and QEMU in-kernel device levels */
uint8_t device_irq_level;
--
2.30.2

View File

@ -0,0 +1,97 @@
From cf832166791bddea562ba9372795db04ea41a581 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Tue, 23 Nov 2021 15:22:27 +0800
Subject: [PATCH 03/28] arm/cpuhp: Add common cpu utility for possible vcpus
Adds various utility functions which might be required to fetch or check the
state of the possible vcpus. This also introduces concept of *disabled* vcpus,
which are part of the *possible* vcpus but are not part of the *present* vcpu.
This state shall be used during machine init time to check the presence of
vcpus.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
cpus-common.c | 19 +++++++++++++++++++
include/hw/core/cpu.h | 21 +++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/cpus-common.c b/cpus-common.c
index 6e73d3e58d..4f0fa42a2e 100644
--- a/cpus-common.c
+++ b/cpus-common.c
@@ -23,6 +23,7 @@
#include "hw/core/cpu.h"
#include "sysemu/cpus.h"
#include "qemu/lockable.h"
+#include "hw/boards.h"
static QemuMutex qemu_cpu_list_lock;
static QemuCond exclusive_cond;
@@ -86,6 +87,24 @@ void cpu_list_add(CPUState *cpu)
QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node);
}
+CPUState *qemu_get_possible_cpu(int index)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ const CPUArchIdList *possible_cpus = ms->possible_cpus;
+ CPUState *cpu;
+
+ assert((index >= 0) && (index < possible_cpus->len));
+
+ cpu = CPU(possible_cpus->cpus[index].cpu);
+
+ return cpu;
+}
+
+bool qemu_present_cpu(CPUState *cpu)
+{
+ return (cpu && !cpu->disabled);
+}
+
void cpu_list_remove(CPUState *cpu)
{
QEMU_LOCK_GUARD(&qemu_cpu_list_lock);
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index bc864564ce..5a2571af3e 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -391,6 +391,7 @@ struct CPUState {
SavedIOTLB saved_iotlb;
#endif
+ bool disabled;
/* TODO Move common fields from CPUArchState here. */
int cpu_index;
int cluster_index;
@@ -749,6 +750,26 @@ static inline bool cpu_in_exclusive_context(const CPUState *cpu)
*/
CPUState *qemu_get_cpu(int index);
+/**
+ * qemu_get_possible_cpu:
+ * @index: The CPUState@cpu_index value of the CPU to obtain.
+ *
+ * Gets a CPU matching @index.
+ *
+ * Returns: The possible CPU or %NULL if there is no matching CPU.
+ */
+CPUState *qemu_get_possible_cpu(int index);
+
+/**
+ * qemu_present_cpu:
+ * @cpu: The vCPU to check
+ *
+ * Checks if the vcpu is amongst the present possible vcpus.
+ *
+ * Returns: True if it is present possible vcpu else false
+ */
+bool qemu_present_cpu(CPUState *cpu);
+
/**
* cpu_exists:
* @id: Guest-exposed CPU ID to lookup.
--
2.30.2

View File

@ -0,0 +1,447 @@
From 0de9776b56a8848f28bdd21332dff50fac12bca4 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Tue, 23 Nov 2021 16:30:39 +0800
Subject: [PATCH 04/28] arm/cpuhp: Machine init time change common to vcpu
{cold|hot}-plug
This refactors (+) introduces the common logic required during the
initialization of both cold and hot plugged vcpus. This also initializes the
*disabled* state of the vcpus which shall be used further during init phases
of various other components like GIC, PMU, ACPI etc as part of the virt machine
initialization.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 226 +++++++++++++++++++++++++++++++++++++++---
include/hw/arm/virt.h | 2 +
target/arm/cpu.c | 7 ++
target/arm/cpu64.c | 8 ++
4 files changed, 228 insertions(+), 15 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 86e1470925..81219d1d5a 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -204,6 +204,8 @@ static const char *valid_cpus[] = {
ARM_CPU_TYPE_NAME("max"),
};
+static CPUArchId *virt_find_cpu_slot(MachineState *ms, int vcpuid);
+
static bool cpu_type_valid(const char *cpu)
{
int i;
@@ -1750,6 +1752,62 @@ static void finalize_gic_version(VirtMachineState *vms)
}
}
+static void virt_cpu_set_properties(Object *cpuobj, const CPUArchId *cpu_slot)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ MemoryRegion *sysmem = get_system_memory();
+ VirtMachineState *vms = VIRT_MACHINE(ms);
+ uint64_t mp_affinity = cpu_slot->arch_id;
+ CPUState *cs = CPU(cpuobj);
+ VirtMachineClass *vmc;
+
+ vmc = VIRT_MACHINE_GET_CLASS(ms);
+
+ /* now, set the cpu object property values */
+ object_property_set_int(cpuobj, "mp-affinity", mp_affinity, NULL);
+
+ numa_cpu_pre_plug(cpu_slot, DEVICE(cpuobj), &error_fatal);
+
+ if (!vms->secure) {
+ object_property_set_bool(cpuobj, "has_el3", false, NULL);
+ }
+
+ if (!vms->virt && object_property_find(cpuobj, "has_el2")) {
+ object_property_set_bool(cpuobj, "has_el2", false, NULL);
+ }
+
+ if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) {
+ object_property_set_int(cpuobj, "psci-conduit", vms->psci_conduit,
+ NULL);
+ /* Secondary CPUs start in PSCI powered-down state */
+ if (cs->cpu_index > 0)
+ object_property_set_bool(cpuobj, "start-powered-off", true,
+ NULL);
+ }
+
+ if (vmc->kvm_no_adjvtime &&
+ object_property_find(cpuobj, "kvm-no-adjvtime")) {
+ object_property_set_bool(cpuobj, "kvm-no-adjvtime", true, NULL);
+ }
+
+ if (vmc->no_pmu && object_property_find(cpuobj, "pmu")) {
+ object_property_set_bool(cpuobj, "pmu", false, NULL);
+ }
+
+ if (object_property_find(cpuobj, "reset-cbar")) {
+ object_property_set_int(cpuobj, "reset-cbar", vms->memmap[VIRT_CPUPERIPHS].base,
+ &error_abort);
+ }
+
+ object_property_set_link(cpuobj, "memory", OBJECT(sysmem),
+ &error_abort);
+
+ if (vms->secure) {
+ object_property_set_link(cpuobj, "secure-memory", OBJECT(vms->secure_sysmem),
+ &error_abort);
+ }
+}
+
/*
* virt_cpu_post_init() must be called after the CPUs have
* been realized and the GIC has been created.
@@ -1867,6 +1925,7 @@ static void machvirt_init(MachineState *machine)
memory_region_init(secure_sysmem, OBJECT(machine), "secure-memory",
UINT64_MAX);
memory_region_add_subregion_overlap(secure_sysmem, 0, sysmem, -1);
+ vms->secure_sysmem = secure_sysmem;
}
firmware_loaded = virt_firmware_init(vms, sysmem,
@@ -1909,6 +1968,15 @@ static void machvirt_init(MachineState *machine)
exit(1);
}
+ vms->max_cpus = max_cpus;
+ if (vms->gic_version < VIRT_GIC_VERSION_3) {
+ warn_report("For GICv%d max-cpus must be equal to smp-cpus",
+ vms->gic_version);
+ warn_report("Overriding specified max-cpus(%d) with smp-cpus(%d)",
+ max_cpus, smp_cpus);
+ vms->max_cpus = smp_cpus;
+ }
+
if (vms->virt && kvm_enabled()) {
error_report("mach-virt: KVM does not support providing "
"Virtualization extensions to the guest CPU");
@@ -1927,14 +1995,14 @@ static void machvirt_init(MachineState *machine)
assert(possible_cpus->len == max_cpus);
for (n = 0; n < possible_cpus->len; n++) {
Object *cpuobj;
- CPUState *cs;
+/* CPUState *cs;
if (n >= smp_cpus) {
break;
}
-
+*/
cpuobj = object_new(possible_cpus->cpus[n].type);
- object_property_set_int(cpuobj, "mp-affinity",
+/* object_property_set_int(cpuobj, "mp-affinity",
possible_cpus->cpus[n].arch_id, NULL);
cs = CPU(cpuobj);
@@ -1942,11 +2010,11 @@ static void machvirt_init(MachineState *machine)
numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj),
&error_fatal);
-
+*/
aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL);
object_property_set_int(cpuobj, "core-id", n, NULL);
- if (!vms->secure) {
+/* if (!vms->secure) {
object_property_set_bool(cpuobj, "has_el3", false, NULL);
}
@@ -1957,9 +2025,9 @@ static void machvirt_init(MachineState *machine)
if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) {
object_property_set_int(cpuobj, "psci-conduit", vms->psci_conduit,
NULL);
-
+*/
/* Secondary CPUs start in PSCI powered-down state */
- if (n > 0) {
+/* if (n > 0) {
object_property_set_bool(cpuobj, "start-powered-off", true,
NULL);
}
@@ -1991,15 +2059,15 @@ static void machvirt_init(MachineState *machine)
object_property_set_link(cpuobj, "secure-memory",
OBJECT(secure_sysmem), &error_abort);
}
-
- if (vms->mte) {
+*/
+// if (vms->mte) {
/* Create the memory region only once, but link to all cpus. */
- if (!tag_sysmem) {
+// if (!tag_sysmem) {
/*
* The property exists only if MemTag is supported.
* If it is, we must allocate the ram to back that up.
*/
- if (!object_property_find(cpuobj, "tag-memory")) {
+/* if (!object_property_find(cpuobj, "tag-memory")) {
error_report("MTE requested, but not supported "
"by the guest CPU");
exit(1);
@@ -2013,9 +2081,9 @@ static void machvirt_init(MachineState *machine)
secure_tag_sysmem = g_new(MemoryRegion, 1);
memory_region_init(secure_tag_sysmem, OBJECT(machine),
"secure-tag-memory", UINT64_MAX / 32);
-
+*/
/* As with ram, secure-tag takes precedence over tag. */
- memory_region_add_subregion_overlap(secure_tag_sysmem, 0,
+/* memory_region_add_subregion_overlap(secure_tag_sysmem, 0,
tag_sysmem, -1);
}
}
@@ -2028,7 +2096,7 @@ static void machvirt_init(MachineState *machine)
&error_abort);
}
}
-
+*/
qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
object_unref(cpuobj);
}
@@ -2382,6 +2450,71 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
return ms->possible_cpus;
}
+static int virt_archid_cmp(const void *a, const void *b)
+{
+ CPUArchId *archid_a = (CPUArchId *)a;
+ CPUArchId *archid_b = (CPUArchId *)b;
+
+ return archid_a->arch_id - archid_b->arch_id;
+}
+
+static CPUArchId *virt_find_cpu_slot(MachineState *ms, int vcpuid)
+{
+ VirtMachineState *vms = VIRT_MACHINE(ms);
+ CPUArchId arch_id, *found_cpu;
+ uint64_t mp_affinity;
+
+ mp_affinity = virt_cpu_mp_affinity(vms, vcpuid);
+ arch_id.arch_id = mp_affinity;
+ found_cpu = bsearch(&arch_id, ms->possible_cpus->cpus,
+ ms->possible_cpus->len,
+ sizeof(*ms->possible_cpus->cpus), virt_archid_cmp);
+
+ assert (found_cpu);
+
+ /*
+ * RFC: Question:
+ * For KVM/TCG, MPIDR for vcpu is derived using vcpu-id.
+ * In fact, as of now there is a linear relation between
+ * vcpu-id and mpidr(see below fig.) as derived in host
+ * kvm. Slot-id is the index where vcpu with certain
+ * arch-id(=mpidr/ap-affinity) is plugged.
+ *
+ * Therefore, for now we could use the vcpu-id as slot
+ * index for getting CPUArchId of the vcpu coresponding
+ * to this slot(this view is not perfectly consistent
+ * with the ARM specification view of MPIDR_EL1).
+ * QEMU/KVM view of cpu topology makes it bit difficult
+ * to use topo-info(pkg-id, core-id, thread-id) with
+ * device_add/-device interface which might not match
+ * with what actual underlying host cpu supports.
+ * therefore question is do we care about this? and
+ * is it okay to have view of thread-id inconsistent
+ * with the host cpu? How should QEMU create PPTT
+ * for the Guest?
+ *
+ * +----+----+----+----+----+----+----+----+
+ * MASK | F F | F F | F F | 0 F |
+ * +----+----+----+----+----+----+----+----+
+ *
+ * | | cluster | cluster | |core|
+ * |<---------Package-id-------->| |core|
+ *
+ * +----+----+----+----+----+----+----+----+
+ * MPIDR ||| Res | Aff2 | Aff1 | Aff0 |
+ * +----+----+----+----+----+----+----+----+
+ * \ \ \ | |
+ * \ 8bit \ 8bit \ |4bit|
+ * \<------->\<------->\ |<-->|
+ * \ \ \| |
+ * +----+----+----+----+----+----+----+----+
+ * VCPU-ID | Byte4 | Byte2 | Byte1 | Byte0 |
+ * +----+----+----+----+----+----+----+----+
+ */
+
+ return found_cpu;
+}
+
static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
@@ -2425,6 +2558,64 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev,
dev, &error_abort);
}
+static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ MachineState *ms = MACHINE(hotplug_dev);
+ ARMCPU *cpu = ARM_CPU(dev);
+ CPUState *cs = CPU(dev);
+ CPUArchId *cpu_slot;
+
+ /* sanity check the cpu */
+ if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
+ error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
+ ms->cpu_type);
+ return;
+ }
+
+ if ((cpu->core_id < 0) || (cpu->core_id >= ms->possible_cpus->len)) {
+ error_setg(errp, "Invalid core-id %u specified, must be in range 1:%u",
+ cpu->core_id, ms->possible_cpus->len - 1);
+ return;
+ }
+
+ /*
+ * RFC: Question:
+ * For now we are not taking into account of other topo info like
+ * thread-id, socket-id to generate arch-id/mp-affinity.
+ * The way KVM/Host generates mpidr value and the way ARM spec
+ * identifies uniquely cpu within the heirarchy is bit inconsistent.
+ * Perhaps needs more discussion on this? Hence, directly using
+ * core_id as cpu_index for now. Ideally, slot-index found out using
+ * the topo info should have been the cpu-index.
+ */
+ cs->cpu_index = cpu->core_id;
+
+ cpu_slot = virt_find_cpu_slot(ms, cpu->core_id);
+ if (qemu_present_cpu(CPU(cpu_slot->cpu))) {
+ error_setg(errp, "cpu %d with arch-id %" PRIu64 " exists",
+ cpu->core_id, cpu_slot->arch_id);
+ return;
+ }
+ virt_cpu_set_properties(OBJECT(cs), cpu_slot);
+}
+
+static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ MachineState *ms = MACHINE(hotplug_dev);
+ ARMCPU *cpu = ARM_CPU(dev);
+ CPUState *cs = CPU(dev);
+ CPUArchId *cpu_slot;
+
+ /* insert the cold/hot-plugged vcpu in the slot */
+ cpu_slot = virt_find_cpu_slot(ms, cpu->core_id);
+ cpu_slot->cpu = OBJECT(dev);
+
+ cs->disabled = false;
+ return;
+}
+
static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -2432,6 +2623,8 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_memory_pre_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ virt_cpu_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
hwaddr db_start = 0, db_end = 0;
char *resv_prop_str;
@@ -2476,6 +2669,8 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
}
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_memory_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ virt_cpu_plug(hotplug_dev, dev, errp);
}
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
PCIDevice *pdev = PCI_DEVICE(dev);
@@ -2556,7 +2751,8 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
MachineClass *mc = MACHINE_GET_CLASS(machine);
if (device_is_dynamic_sysbus(mc, dev) ||
- (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
+ (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) ||
+ (object_dynamic_cast(OBJECT(dev), TYPE_CPU))) {
return HOTPLUG_HANDLER(machine);
}
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 9661c46699..960812c66e 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -137,6 +137,7 @@ struct VirtMachineState {
DeviceState *platform_bus_dev;
FWCfgState *fw_cfg;
PFlashCFI01 *flash[2];
+ MemoryRegion *secure_sysmem;
bool secure;
bool highmem;
bool highmem_ecam;
@@ -155,6 +156,7 @@ struct VirtMachineState {
char *pciehb_nodename;
const int *irqmap;
int fdt_size;
+ int max_cpus;
uint32_t clock_phandle;
uint32_t gic_phandle;
uint32_t msi_phandle;
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 5dc3fa6c3a..ff827d56b7 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2004,6 +2004,12 @@ static const struct TCGCPUOps arm_tcg_ops = {
};
#endif /* CONFIG_TCG */
+static int64_t arm_cpu_get_arch_id(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ return cpu->mp_affinity;
+}
+
static void arm_cpu_class_init(ObjectClass *oc, void *data)
{
ARMCPUClass *acc = ARM_CPU_CLASS(oc);
@@ -2019,6 +2025,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
cc->class_by_name = arm_cpu_class_by_name;
cc->has_work = arm_cpu_has_work;
cc->dump_state = arm_cpu_dump_state;
+ cc->get_arch_id = arm_cpu_get_arch_id;
cc->set_pc = arm_cpu_set_pc;
cc->gdb_read_register = arm_cpu_gdb_read_register;
cc->gdb_write_register = arm_cpu_gdb_write_register;
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index c690318a9b..2134be0b67 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -894,7 +894,10 @@ static gchar *aarch64_gdb_arch_name(CPUState *cs)
static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
{
CPUClass *cc = CPU_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ CPUState *cs = CPU(oc);
+ dc->user_creatable = true;
cc->gdb_read_register = aarch64_cpu_gdb_read_register;
cc->gdb_write_register = aarch64_cpu_gdb_write_register;
cc->gdb_num_core_regs = 34;
@@ -906,6 +909,11 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc, "aarch64",
"Set on/off to enable/disable aarch64 "
"execution state ");
+ /*
+ * we start every ARM64 vcpu as disabled possible vcpu. It needs to be
+ * enabled explicitly
+ */
+ cs->disabled = true;
}
static void aarch64_cpu_instance_init(Object *obj)
--
2.30.2

View File

@ -0,0 +1,243 @@
From 1d9605a449833e464bd0388bb658d31a84ad4c7d Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Wed, 24 Nov 2021 15:30:20 +0800
Subject: [PATCH 05/28] arm/cpuhp: Pre-create disabled possible vcpus @machine
init
In ARMv8 architecture, GIC needs all the vcpus to be created and present when
it is initialized. This is because:
1. GICC and MPIDR association must be fixed at the VM initialization time.
This is represented by register GIC_TYPER(mp_afffinity, proc_num)
2. GICC(cpu interfaces), GICR(redistributors) etc all must be initialized
at the boot time as well.
3. Memory regions associated with GICR etc. cannot be changed(add/del/mod)
after VM has inited.
This patch adds the support to pre-create all such possible vcpus within the
host using the KVM interface as part of the virt machine initialization. These
vcpus could later be attached to QOM/ACPI while they are actually hot plugged
and made present.
NOTE: There is some refactoring related to the kvm_destroy_vcpu/kvm_get_vcpu
(to make use of the common code) has been intentionaly left out in RFC
version to avoid obscuring the framework change of the cpu hotplug. The
existing code being presented in this patch could further be optimized
later.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
accel/kvm/kvm-all.c | 31 +++++++++++++++++++++++++++
accel/kvm/kvm-cpus.h | 2 ++
hw/arm/virt.c | 50 +++++++++++++++++++++++++++++++++++++++++---
target/arm/kvm.c | 33 +++++++++++++++++++++++++++++
target/arm/kvm_arm.h | 11 ++++++++++
5 files changed, 124 insertions(+), 3 deletions(-)
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 0125c17edb..46717dad14 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -425,6 +425,37 @@ err:
return ret;
}
+void kvm_park_vcpu(CPUState *cs)
+{
+ unsigned long vcpu_id = cs->cpu_index;
+ struct KVMParkedVcpu *vcpu;
+
+ vcpu = g_malloc0(sizeof(*vcpu));
+ vcpu->vcpu_id = vcpu_id;
+ vcpu->kvm_fd = cs->kvm_fd;
+ QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
+}
+
+int kvm_create_vcpu(CPUState *cpu)
+{
+ unsigned long vcpu_id = cpu->cpu_index;
+ KVMState *s = kvm_state;
+ int ret = 0;
+
+ DPRINTF("kvm_create_vcpu\n");
+
+ ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id);
+ if (ret < 0) {
+ goto err;
+ }
+ cpu->kvm_fd = ret;
+ cpu->kvm_state = s;
+ cpu->vcpu_dirty = true;
+
+err:
+ return ret;
+}
+
void kvm_destroy_vcpu(CPUState *cpu)
{
if (do_kvm_destroy_vcpu(cpu) < 0) {
diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h
index bf0bd1bee4..54a4fd19cf 100644
--- a/accel/kvm/kvm-cpus.h
+++ b/accel/kvm/kvm-cpus.h
@@ -14,6 +14,8 @@
int kvm_init_vcpu(CPUState *cpu, Error **errp);
int kvm_cpu_exec(CPUState *cpu);
+int kvm_create_vcpu(CPUState *cpu);
+void kvm_park_vcpu(CPUState *cs);
void kvm_destroy_vcpu(CPUState *cpu);
void kvm_cpu_synchronize_post_reset(CPUState *cpu);
void kvm_cpu_synchronize_post_init(CPUState *cpu);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 81219d1d5a..853288b34a 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1995,13 +1995,14 @@ static void machvirt_init(MachineState *machine)
assert(possible_cpus->len == max_cpus);
for (n = 0; n < possible_cpus->len; n++) {
Object *cpuobj;
-/* CPUState *cs;
+ CPUState *cs;
- if (n >= smp_cpus) {
+/* if (n >= smp_cpus) {
break;
}
*/
cpuobj = object_new(possible_cpus->cpus[n].type);
+ cs = CPU(cpuobj);
/* object_property_set_int(cpuobj, "mp-affinity",
possible_cpus->cpus[n].arch_id, NULL);
@@ -2096,9 +2097,52 @@ static void machvirt_init(MachineState *machine)
&error_abort);
}
}
-*/
+
qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
object_unref(cpuobj);
+*/
+ if (n < smp_cpus) {
+ char *core_id = g_strdup_printf("core%d", n);
+ qdev_set_id(DEVICE(cpuobj),core_id);
+ object_property_set_bool(cpuobj, "realized", true, &error_fatal);
+ g_free(core_id);
+ object_unref(OBJECT(cs));
+ } else {
+ CPUArchId *cpu_slot;
+ /* handling for vcpus which are yet to be hot-plugged */
+ cs->cpu_index = n; cs->disabled=true;
+ /* ARM host vcpu features need to be fixed at the boot time */
+ virt_cpu_set_properties(cpuobj, &possible_cpus->cpus[n]);
+ /*
+ * For KVM, we shall be pre-creating the now disabled/un-plugged
+ * possbile host vcpus and park them till the time they are
+ * actually hot plugged. This is required to pre-size the host
+ * GICC and GICR with the all possible vcpus for this VM.
+ */
+ if (kvm_enabled()) {
+ kvm_arm_create_host_vcpu(ARM_CPU(cs));
+ }
+ /*
+ * Add disabled vcpu to cpu slot during the init phase of the virt machine.
+ * 1. We need this ARMCPU object during the GIC init. This object
+ * will facilitate in pre-realizing the gic. Any info like
+ * mp-affinity(required to derive gicr_type) etc could still be
+ * fetched while preserving QOM abstraction akin to realized
+ * vcpus.
+ * 2. Now, after initialization of the virt machine is complete we could use
+ * two approaches to deal with this ARMCPU object:
+ * (i) re-use this ARMCPU object during hotplug of this vcpu.
+ * OR
+ * (ii) defer release this ARMCPU object after gic has been
+ * initialized or during pre-plug phase when a vcpu is
+ * hotplugged.
+ *
+ * We will use the (ii) approach and release the ARMCPU objects after GIC
+ * and machine has been initialized in machine_init_done() phase
+ */
+ cpu_slot = virt_find_cpu_slot(machine, cs->cpu_index);
+ cpu_slot->cpu = OBJECT(cs);
+ }
}
fdt_add_timer_nodes(vms);
fdt_add_cpu_nodes(vms);
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index d8381ba224..bb75beec9d 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -32,6 +32,7 @@
#include "hw/boards.h"
#include "hw/irq.h"
#include "qemu/log.h"
+#include "accel/kvm/kvm-cpus.h"
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
KVM_CAP_LAST_INFO
@@ -627,6 +628,38 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu)
write_list_to_cpustate(cpu);
}
+void kvm_arm_create_host_vcpu(ARMCPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ unsigned long vcpu_id = cs->cpu_index;
+ int ret;
+
+ ret = kvm_create_vcpu(cs);
+ if (ret < 0) {
+ error_report("Failed to create host vcpu %ld", vcpu_id);
+ abort();
+ }
+
+ /*
+ * Initialize the vcpu in the host. This will reset the sys regs
+ * for this vcpu and related registers like MPIDR_EL1 etc. also
+ * gets programmed during this call to host. These are referred
+ * later while setting device attributes of the GICR during GICv3
+ * reset
+ */
+ ret = kvm_arch_init_vcpu(cs);
+ if (ret < 0) {
+ error_report("Failed to initialize host vcpu %ld", vcpu_id);
+ abort();
+ }
+
+ /*
+ * park the created vcpu. shall be used during kvm_get_vcpu() when
+ * threads are created during realization of ARM vcpus
+ */
+ kvm_park_vcpu(cs);
+}
+
/*
* Update KVM's MP_STATE based on what QEMU thinks it is
*/
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index 34f8daa377..e0490c97b5 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -155,6 +155,17 @@ void kvm_arm_cpu_post_load(ARMCPU *cpu);
*/
void kvm_arm_reset_vcpu(ARMCPU *cpu);
+/**
+ * kvm_arm_create_host_vcpu:
+ * @cpu: ARMCPU
+ *
+ * Called at to pre create all possible kvm vcpus within the the host at the
+ * virt machine init time. This will also init this pre-created vcpu and
+ * hence result in vcpu reset at host. These pre created and inited vcpus
+ * shall be parked for use when ARM vcpus are actually realized.
+ */
+void kvm_arm_create_host_vcpu(ARMCPU *cpu);
+
/**
* kvm_arm_init_serror_injection:
* @cs: CPUState
--
2.30.2

View File

@ -0,0 +1,220 @@
From b588545bf1bb168eb0853ae36525d5407657eb7b Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Wed, 24 Nov 2021 16:09:08 +0800
Subject: [PATCH 06/28] arm/cpuhp: Changes to pre-size GIC with possible vcpus
@machine init
GIC needs to be pre-sized with possible vcpus at the initialization time. This
is necessary because Memory regions and resources associated with GICC/GICR
etc cannot be changed (add/del/modified) after VM has inited. Also, GIC_TYPER
needs to be initialized with mp_affinity and cpu interface number association.
This cannot be changed after GIC has initialized.
Once all the cpu interfaces of the GIC has been inited it needs to be ensured
that any updations to the GICC during reset only takes place for the present
vcpus and not the disabled ones. Therefore, proper checks are required at
various places.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 15 ++++++++-------
hw/intc/arm_gicv3_common.c | 8 ++++++--
hw/intc/arm_gicv3_cpuif.c | 6 ++++++
hw/intc/arm_gicv3_kvm.c | 31 ++++++++++++++++++++++++++++---
include/hw/arm/virt.h | 2 +-
5 files changed, 49 insertions(+), 13 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 853288b34a..1b28687883 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -630,13 +630,14 @@ static void create_gic(VirtMachineState *vms)
const char *gictype;
int type = vms->gic_version, i;
unsigned int smp_cpus = ms->smp.cpus;
+ unsigned int max_cpus = vms->max_cpus;
uint32_t nb_redist_regions = 0;
gictype = (type == 3) ? gicv3_class_name() : gic_class_name();
vms->gic = qdev_new(gictype);
qdev_prop_set_uint32(vms->gic, "revision", type);
- qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus);
+ qdev_prop_set_uint32(vms->gic, "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).
*/
@@ -648,7 +649,7 @@ static void create_gic(VirtMachineState *vms)
if (type == 3) {
uint32_t redist0_capacity =
vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
- uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);
+ uint32_t redist0_count = MIN(max_cpus, redist0_capacity);
nb_redist_regions = virt_gicv3_redist_region_count(vms);
@@ -661,7 +662,7 @@ static void create_gic(VirtMachineState *vms)
vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
qdev_prop_set_uint32(vms->gic, "redist-region-count[1]",
- MIN(smp_cpus - redist0_count, redist1_capacity));
+ MIN(max_cpus - redist0_count, redist1_capacity));
}
} else {
if (!kvm_irqchip_in_kernel()) {
@@ -718,7 +719,7 @@ static void create_gic(VirtMachineState *vms)
} else if (vms->virt) {
qemu_irq irq = qdev_get_gpio_in(vms->gic,
ppibase + ARCH_GIC_MAINT_IRQ);
- sysbus_connect_irq(gicbusdev, i + 4 * smp_cpus, irq);
+ sysbus_connect_irq(gicbusdev, i + 4 * max_cpus, irq);
}
qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0,
@@ -726,11 +727,11 @@ static void create_gic(VirtMachineState *vms)
+ VIRTUAL_PMU_IRQ));
sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
- sysbus_connect_irq(gicbusdev, i + smp_cpus,
+ sysbus_connect_irq(gicbusdev, i + max_cpus,
qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
- sysbus_connect_irq(gicbusdev, i + 2 * smp_cpus,
+ sysbus_connect_irq(gicbusdev, i + 2 * max_cpus,
qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
- sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus,
+ sysbus_connect_irq(gicbusdev, i + 3 * max_cpus,
qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
}
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 58ef65f589..cfc112e43e 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -348,11 +348,15 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
s->cpu = g_new0(GICv3CPUState, s->num_cpu);
for (i = 0; i < s->num_cpu; i++) {
- CPUState *cpu = qemu_get_cpu(i);
+ CPUState *cpu = qemu_get_possible_cpu(i);
uint64_t cpu_affid;
int last;
- s->cpu[i].cpu = cpu;
+ if (qemu_present_cpu(cpu))
+ s->cpu[i].cpu = cpu;
+ else
+ s->cpu[i].cpu = NULL;
+
s->cpu[i].gic = s;
/* Store GICv3CPUState in CPUARMState gicv3state pointer */
gicv3_set_gicv3state(cpu, &s->cpu[i]);
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index a032d505f5..819c032ec5 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -781,6 +781,9 @@ void gicv3_cpuif_update(GICv3CPUState *cs)
ARMCPU *cpu = ARM_CPU(cs->cpu);
CPUARMState *env = &cpu->env;
+ if (!qemu_present_cpu(cs->cpu))
+ return;
+
g_assert(qemu_mutex_iothread_locked());
trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq,
@@ -1674,6 +1677,9 @@ static void icc_generate_sgi(CPUARMState *env, GICv3CPUState *cs,
for (i = 0; i < s->num_cpu; i++) {
GICv3CPUState *ocs = &s->cpu[i];
+ if (!qemu_present_cpu(ocs->cpu))
+ continue;
+
if (irm) {
/* IRM == 1 : route to all CPUs except self */
if (cs == ocs) {
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 5c09f00dec..4e7bb4ac1f 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -24,6 +24,7 @@
#include "hw/intc/arm_gicv3_common.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
+#include "sysemu/cpus.h"
#include "sysemu/kvm.h"
#include "sysemu/runstate.h"
#include "kvm_arm.h"
@@ -456,6 +457,17 @@ static void kvm_arm_gicv3_put(GICv3State *s)
GICv3CPUState *c = &s->cpu[ncpu];
int num_pri_bits;
+ /*
+ * To support hotplug of vcpus we need to make sure all gic cpuif/GICC
+ * are initialized at machvirt init time. Once the init is done we
+ * release the ARMCPU object for disabled vcpus but this leg could hit
+ * during reset of GICC later as well i.e. after init has happened and
+ * all of the cases we want to make sure we dont acess the GICC for
+ * the disabled VCPUs.
+ */
+ if (!qemu_present_cpu(c->cpu))
+ continue;
+
kvm_gicc_access(s, ICC_SRE_EL1, ncpu, &c->icc_sre_el1, true);
kvm_gicc_access(s, ICC_CTLR_EL1, ncpu,
&c->icc_ctlr_el1[GICV3_NS], true);
@@ -683,11 +695,24 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
return;
}
+ /*
+ * This shall be called even when vcpu is being hotplugged and other vcpus
+ * might be running. Host kernel KVM code to handle device access of IOCTLs
+ * KVM_{GET|SET}_DEVICE_ATTR might fail due to inability to grab vcpu locks
+ * for all the vcpus. Hence, we need to pause all vcpus to facilitate
+ * locking within host.
+ */
+ if (!qemu_present_cpu(c->cpu))
+ pause_all_vcpus();
+
/* Initialize to actual HW supported configuration */
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
KVM_VGIC_ATTR(ICC_CTLR_EL1, c->gicr_typer),
&c->icc_ctlr_el1[GICV3_NS], false, &error_abort);
+ if (!qemu_present_cpu(c->cpu))
+ resume_all_vcpus();
+
c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS];
}
@@ -794,9 +819,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
}
for (i = 0; i < s->num_cpu; i++) {
- ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i));
-
- define_arm_cp_regs(cpu, gicv3_cpuif_reginfo);
+ CPUState *cs = qemu_get_cpu(i);
+ if (qemu_present_cpu(cs))
+ define_arm_cp_regs(ARM_CPU(cs), gicv3_cpuif_reginfo);
}
/* Try to create the device via the device control API */
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 960812c66e..6233be9590 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -187,7 +187,7 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
assert(vms->gic_version == VIRT_GIC_VERSION_3);
- return MACHINE(vms)->smp.cpus > redist0_capacity ? 2 : 1;
+ return vms->max_cpus > redist0_capacity ? 2 : 1;
}
#endif /* QEMU_ARM_VIRT_H */
--
2.30.2

View File

@ -0,0 +1,95 @@
From b885e6d6e6a1199a6db17fb5753df2ca63c611b5 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Wed, 24 Nov 2021 16:21:39 +0800
Subject: [PATCH 07/28] arm/cpuhp: Init PMU at host for all possible vcpus
PMU for all possible vcpus must be initialized at the virt machine
initialization time. This patch refactors existing code to accomodate possible
vcpus. This also assumes that all processor being used are identical at least
for now but does not affect the normal scanarios where they might not be in
future. This assumption only affects the future hotplug scenarios if ever there
exists any hetergenous processors. In such a case PMU might not be enabled on
some vcpus. Is it acceptable and doable tradeoff for now?
This perhaps needs more discussion. please check below link,
Link: https://lists.gnu.org/archive/html/qemu-devel/2020-06/msg00131.html
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 38 ++++++++++++++++++++++++++++++++++++--
include/hw/arm/virt.h | 1 +
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 1b28687883..61fc431d20 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1502,6 +1502,38 @@ static void create_secure_ram(VirtMachineState *vms,
g_free(nodename);
}
+/*static bool virt_pmu_init(VirtMachineState *vms)
+{
+ CPUArchIdList *possible_cpus = vms->parent.possible_cpus;
+ ARMCPU *armcpu;
+ int n; */
+
+ /*
+ * As of now KVM ensures that within the host all the vcpus have same
+ * features configured. This cannot be changed later and cannot be diferent
+ * for new vcpus being plugged in. Also, -cpu option/virt machine cpu-type
+ * ensures all the vcpus are identical.
+ */
+/* for (n = 0; n < possible_cpus->len; n++) {
+ CPUState *cpu = qemu_get_possible_cpu(n);
+ armcpu = ARM_CPU(cpu);
+
+ if (!arm_feature(&armcpu->env, ARM_FEATURE_PMU)) {
+ warn_report("Not all vcpus might have PMU initialized");
+ return false;
+ }
+
+ if (kvm_enabled()) {
+ if (kvm_irqchip_in_kernel()) {
+ kvm_arm_pmu_set_irq(cpu, PPI(VIRTUAL_PMU_IRQ));
+ }
+ kvm_arm_pmu_init(cpu);
+ }
+ }
+
+ return true;
+}*/
+
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
{
const VirtMachineState *board = container_of(binfo, VirtMachineState,
@@ -2161,8 +2193,10 @@ static void machvirt_init(MachineState *machine)
virt_cpu_post_init(vms, sysmem);
- fdt_add_pmu_nodes(vms);
-
+// if (!vmc->no_pmu && virt_pmu_init(vms)) {
+// vms->pmu = true;
+ fdt_add_pmu_nodes(vms);
+// }
create_uart(vms, VIRT_UART, sysmem, serial_hd(0));
if (vms->secure) {
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 6233be9590..a568420303 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -144,6 +144,7 @@ struct VirtMachineState {
bool its;
bool virt;
bool ras;
+ bool pmu;
bool mte;
OnOffAuto acpi;
VirtGICType gic_version;
--
2.30.2

View File

@ -0,0 +1,115 @@
From 8cdfd18d515aa5be1c54061e42f1c8a69997667b Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Thu, 25 Nov 2021 13:58:25 +0800
Subject: [PATCH 08/28] arm/cpuhp: Enable ACPI support for vcpu hotplug
ACPI is required to interface QEMU with the guest. Roughly falls into below
cases,
1. Convey the possible vcpus config at the machine init time to the guest
using various DSDT tables like MADT etc.
2. Convey vcpu hotplug events to guest(using GED)
3. Assist in evaluation of various ACPI methods(like _EVT, _STA, _OST, _EJ0,
_MAT etc.)
4. Provides ACPI cpu hotplug state and 12 Byte memory mapped cpu hotplug
control register interface to the OSPM/guest corresponding to each possible
vcpu. The register interface consists of various R/W fields and their
handling operations. These are called when ever register fields or memory
regions are accessed(i.e. read or written) by OSPM when ever it evaluates
various ACPI methods.
Note: lot of this framework code is inherited from the changes already done for
x86 but still some minor changes are required to make it compatible with
ARM64.)
This patch enables the ACPI support for virtual cpu hotplug in kconfig and
during initialization.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/acpi/cpu.c | 6 +++++-
hw/arm/Kconfig | 1 +
hw/arm/virt.c | 2 ++
include/hw/acpi/cpu_hotplug.h | 2 ++
include/hw/arm/virt.h | 1 +
5 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c
index f82e9512fd..cf07a6c30c 100644
--- a/hw/acpi/cpu.c
+++ b/hw/acpi/cpu.c
@@ -226,7 +226,11 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner,
state->dev_count = id_list->len;
state->devs = g_new0(typeof(*state->devs), state->dev_count);
for (i = 0; i < id_list->len; i++) {
- state->devs[i].cpu = CPU(id_list->cpus[i].cpu);
+ struct CPUState *cpu = CPU(id_list->cpus[i].cpu);
+ if (qemu_present_cpu(cpu))
+ state->devs[i].cpu = cpu;
+ else
+ state->devs[i].cpu = NULL;
state->devs[i].arch_id = id_list->cpus[i].arch_id;
}
memory_region_init_io(&state->ctrl_reg, owner, &cpu_hotplug_ops, state,
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 4ba0aca067..32b150676a 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -29,6 +29,7 @@ config ARM_VIRT
select ACPI_HW_REDUCED
select ACPI_NVDIMM
select ACPI_APEI
+ select ACPI_CPU_HOTPLUG
config CHEETAH
bool
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 61fc431d20..4265c0e2e8 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -77,6 +77,7 @@
#include "hw/virtio/virtio-iommu.h"
#include "hw/char/pl011.h"
#include "qemu/guest-random.h"
+#include "hw/acpi/cpu_hotplug.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -152,6 +153,7 @@ static const MemMapEntry base_memmap[] = {
[VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN},
[VIRT_PVTIME] = { 0x090a0000, 0x00010000 },
[VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 },
+ [VIRT_CPUHP_ACPI] = { 0x090c0000, ACPI_CPU_HOTPLUG_REG_LEN},
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
diff --git a/include/hw/acpi/cpu_hotplug.h b/include/hw/acpi/cpu_hotplug.h
index 3b932abbbb..48b291e45e 100644
--- a/include/hw/acpi/cpu_hotplug.h
+++ b/include/hw/acpi/cpu_hotplug.h
@@ -19,6 +19,8 @@
#include "hw/hotplug.h"
#include "hw/acpi/cpu.h"
+#define ACPI_CPU_HOTPLUG_REG_LEN 12
+
typedef struct AcpiCpuHotplug {
Object *device;
MemoryRegion io;
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index a568420303..8954b78c27 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -85,6 +85,7 @@ enum {
VIRT_PCDIMM_ACPI,
VIRT_ACPI_GED,
VIRT_NVDIMM_ACPI,
+ VIRT_CPUHP_ACPI,
VIRT_PVTIME,
VIRT_LOWMEMMAP_LAST,
};
--
2.30.2

View File

@ -0,0 +1,101 @@
From d184f34dbfc972b0a27be189ce8c1a75e400c920 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Thu, 25 Nov 2021 16:31:07 +0800
Subject: [PATCH 09/28] arm/cpuhp: Init GED framework with cpu hotplug events
ACPI GED(as described in the ACPI 6.2 spec) can be used to generate ACPI events
when OSPM/guest receives an interrupt listed in the _CRS object of GED. OSPM
then maps or demultiplexes the event by evaluating _EVT method.
This change adds the support of cpu hotplug event initialization in the
existing GED framework.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/acpi/generic_event_device.c | 8 ++++++++
hw/arm/virt.c | 3 ++-
include/hw/acpi/generic_event_device.h | 5 +++++
3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index e28457a7d1..1ed7623907 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -25,6 +25,7 @@ static const uint32_t ged_supported_events[] = {
ACPI_GED_MEM_HOTPLUG_EVT,
ACPI_GED_PWR_DOWN_EVT,
ACPI_GED_NVDIMM_HOTPLUG_EVT,
+ ACPI_GED_CPU_HOTPLUG_EVT,
};
/*
@@ -390,6 +391,13 @@ static void acpi_ged_initfn(Object *obj)
acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev),
&s->memhp_state, 0);
+ s->cpuhp.device = OBJECT(s);
+ memory_region_init(&s->container_cpuhp, OBJECT(dev), "cpuhp container",
+ ACPI_CPU_HOTPLUG_REG_LEN);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container_cpuhp);
+ cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev),
+ &s->cpuhp_state, 0);
+
memory_region_init_io(&ged_st->regs, obj, &ged_regs_ops, ged_st,
TYPE_ACPI_GED "-regs", ACPI_GED_REG_COUNT);
sysbus_init_mmio(sbd, &ged_st->regs);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 4265c0e2e8..47db084183 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -560,7 +560,7 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
DeviceState *dev;
MachineState *ms = MACHINE(vms);
int irq = vms->irqmap[VIRT_ACPI_GED];
- uint32_t event = ACPI_GED_PWR_DOWN_EVT;
+ uint32_t event = ACPI_GED_PWR_DOWN_EVT | ACPI_GED_CPU_HOTPLUG_EVT;
if (ms->ram_slots) {
event |= ACPI_GED_MEM_HOTPLUG_EVT;
@@ -575,6 +575,7 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, vms->memmap[VIRT_CPUHP_ACPI].base);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(vms->gic, irq));
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
index 6bed92e8fc..454bf08da2 100644
--- a/include/hw/acpi/generic_event_device.h
+++ b/include/hw/acpi/generic_event_device.h
@@ -63,6 +63,7 @@
#include "hw/acpi/memory_hotplug.h"
#include "hw/acpi/ghes.h"
#include "qom/object.h"
+#include "hw/acpi/cpu_hotplug.h"
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
@@ -75,6 +76,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED)
#define ACPI_GED_EVT_SEL_OFFSET 0x0
#define ACPI_GED_EVT_SEL_LEN 0x4
+#define ACPI_GED_CPU_HOTPLUG_EVT 0x8
#define ACPI_GED_REG_SLEEP_CTL 0x00
#define ACPI_GED_REG_SLEEP_STS 0x01
@@ -110,6 +112,9 @@ struct AcpiGedState {
SysBusDevice parent_obj;
MemHotplugState memhp_state;
MemoryRegion container_memhp;
+ CPUHotplugState cpuhp_state;
+ MemoryRegion container_cpuhp;
+ AcpiCpuHotplug cpuhp;
GEDState ged_state;
uint32_t ged_event_bitmap;
qemu_irq irq;
--
2.30.2

View File

@ -0,0 +1,139 @@
From f7b9c727b0da0bec444d85bd1bf7bd5bfb9d3e4f Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Thu, 25 Nov 2021 16:54:49 +0800
Subject: [PATCH 10/28] arm/cpuhp: Update CPUs AML with cpu-(ctrl)dev change
CPUs Control device(\\_SB.PCI0) register interface for the x86 arch is based on
PCI and is IO port based and hence existing cpus AML code assumes _CRS objects
would evaluate to a system resource which describes IO Port address. But on ARM
arch CPUs control device(\\_SB.PRES) register interface is memory-mapped hence
_CRS object should evaluate to system resource which describes memory-mapped
base address.
This cpus AML code change updates the existing inerface of the build cpus AML
function to accept both IO/MEMORY type regions and update the _CRS object
correspondingly.
NOTE: Beside above CPU scan shall be triggered when OSPM evaluates _EVT method
part of the GED framework which is covered in subsequent patch.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/acpi/cpu.c | 23 +++++++++++++++--------
hw/arm/virt-acpi-build.c | 13 ++++++++++++-
hw/i386/acpi-build.c | 2 +-
include/hw/acpi/cpu.h | 5 +++--
4 files changed, 31 insertions(+), 12 deletions(-)
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c
index cf07a6c30c..98657ad28b 100644
--- a/hw/acpi/cpu.c
+++ b/hw/acpi/cpu.c
@@ -345,9 +345,10 @@ const VMStateDescription vmstate_cpu_hotplug = {
#define CPU_FW_EJECT_EVENT "CEJF"
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
- hwaddr io_base,
+ hwaddr mmap_io_base,
const char *res_root,
- const char *event_handler_method)
+ const char *event_handler_method,
+ AmlRegionSpace rs)
{
Aml *ifctx;
Aml *field;
@@ -375,13 +376,18 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0));
crs = aml_resource_template();
- aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1,
+ if (rs == AML_SYSTEM_IO) {
+ aml_append(crs, aml_io(AML_DECODE16, mmap_io_base, mmap_io_base, 1,
ACPI_CPU_HOTPLUG_REG_LEN));
+ } else {
+ aml_append(crs, aml_memory32_fixed(mmap_io_base,
+ ACPI_CPU_HOTPLUG_REG_LEN, AML_READ_WRITE));
+ }
aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs));
/* declare CPU hotplug MMIO region with related access fields */
aml_append(cpu_ctrl_dev,
- aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base),
+ aml_operation_region("PRST", rs, aml_int(mmap_io_base),
ACPI_CPU_HOTPLUG_REG_LEN));
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK,
@@ -720,9 +726,10 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
aml_append(sb_scope, cpus_dev);
aml_append(table, sb_scope);
- method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
- aml_append(method, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD));
- aml_append(table, method);
-
+ if (event_handler_method) {
+ method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD));
+ aml_append(table, method);
+ }
g_free(cphp_res_path);
}
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 037cc1fd82..95a3c3b31b 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -703,7 +703,18 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
* the RTC ACPI device at all when using UEFI.
*/
scope = aml_scope("\\_SB");
- acpi_dsdt_add_cpus(scope, vms);
+ /* if GED is enabled then cpus AML shall be added as part build_cpus_aml */
+ if (vms->acpi_dev) {
+ CPUHotplugFeatures opts = {
+ .acpi_1_compatible = false,
+ .has_legacy_cphp = false
+ };
+
+ build_cpus_aml(scope, ms, opts, memmap[VIRT_CPUHP_ACPI].base,
+ "\\_SB", NULL, AML_SYSTEM_MEMORY);
+ } else {
+ acpi_dsdt_add_cpus(scope, vms);
+ }
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
(irqmap[VIRT_UART] + ARM_SPI_BASE));
if (vmc->acpi_expose_flash) {
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index a33ac8b91e..fa7a5ed79d 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1503,7 +1503,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
.fw_unplugs_cpu = pm->smi_on_cpu_unplug,
};
build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base,
- "\\_SB.PCI0", "\\_GPE._E02");
+ "\\_SB.PCI0", "\\_GPE._E02", AML_SYSTEM_IO);
}
if (pcms->memhp_io_base && nr_mem) {
diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h
index 999caaf510..ead8f1d1cc 100644
--- a/include/hw/acpi/cpu.h
+++ b/include/hw/acpi/cpu.h
@@ -56,9 +56,10 @@ typedef struct CPUHotplugFeatures {
} CPUHotplugFeatures;
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
- hwaddr io_base,
+ hwaddr mmap_io_base,
const char *res_root,
- const char *event_handler_method);
+ const char *event_handler_method,
+ AmlRegionSpace rs);
void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list);
--
2.30.2

View File

@ -0,0 +1,51 @@
From e9a06ca70bbb956ec96f69e2e7bebc2a7b9044a8 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Thu, 25 Nov 2021 17:38:19 +0800
Subject: [PATCH 11/28] arm/cpuhp: Update GED _EVT method AML with cpu scan
OSPM evaluates _EVT method to map the event. The cpu hotplug event eventually
results in start of the cpu scan. Scan figures out the cpu and the kind of
event(plug/unplug) and notifies it back to the guest.
The change in this patch updates the GED AML _EVT method with the call to
\\_SB.CPUS.CSCN which will do above.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/acpi/generic_event_device.c | 4 ++++
include/hw/acpi/cpu_hotplug.h | 2 ++
2 files changed, 6 insertions(+)
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 1ed7623907..7278b89c6a 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -108,6 +108,10 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "."
MEMORY_SLOT_SCAN_METHOD));
break;
+ case ACPI_GED_CPU_HOTPLUG_EVT:
+ aml_append(if_ctx, aml_call0(ACPI_CPU_CONTAINER "."
+ ACPI_CPU_SCAN_METHOD));
+ break;
case ACPI_GED_PWR_DOWN_EVT:
aml_append(if_ctx,
aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
diff --git a/include/hw/acpi/cpu_hotplug.h b/include/hw/acpi/cpu_hotplug.h
index 48b291e45e..ef631750b4 100644
--- a/include/hw/acpi/cpu_hotplug.h
+++ b/include/hw/acpi/cpu_hotplug.h
@@ -20,6 +20,8 @@
#include "hw/acpi/cpu.h"
#define ACPI_CPU_HOTPLUG_REG_LEN 12
+#define ACPI_CPU_SCAN_METHOD "CSCN"
+#define ACPI_CPU_CONTAINER "\\_SB.CPUS"
typedef struct AcpiCpuHotplug {
Object *device;
--
2.30.2

View File

@ -0,0 +1,67 @@
From 323ce9267470a6e0bf22c9b19399f12804524d78 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Thu, 25 Nov 2021 17:52:12 +0800
Subject: [PATCH 12/28] arm/cpuhp: MADT Tbl change to size the guest with
possible vcpus
Changes required during building of MADT Table by QEMU to accomodate disabled
possible vcpus. This info shall be used by the guest kernel to size up its
resources during boot time. This pre-sizing of the guest kernel done on
possible vcpus will facilitate hotplug of the disabled vcpus.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt-acpi-build.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 95a3c3b31b..416b43c9f8 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -573,6 +573,8 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
const int *irqmap = vms->irqmap;
AcpiMadtGenericDistributor *gicd;
AcpiMadtGenericMsiFrame *gic_msi;
+ MachineState *ms = &vms->parent;
+ CPUArchIdList *possible_cpus = ms->possible_cpus;
int i;
acpi_data_push(table_data, sizeof(AcpiMultipleApicTable));
@@ -583,11 +585,10 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
gicd->base_address = cpu_to_le64(memmap[VIRT_GIC_DIST].base);
gicd->version = vms->gic_version;
- for (i = 0; i < MACHINE(vms)->smp.cpus; i++) {
+ for (i = 0; i < MACHINE(vms)->smp.max_cpus; i++) {
AcpiMadtGenericCpuInterface *gicc = acpi_data_push(table_data,
sizeof(*gicc));
- ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i));
-
+ ARMCPU *cpu = ARM_CPU(qemu_get_possible_cpu(i));
gicc->type = ACPI_APIC_GENERIC_CPU_INTERFACE;
gicc->length = sizeof(*gicc);
if (vms->gic_version == 2) {
@@ -596,11 +597,14 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
gicc->gicv_base_address = cpu_to_le64(memmap[VIRT_GIC_VCPU].base);
}
gicc->cpu_interface_number = cpu_to_le32(i);
- gicc->arm_mpidr = cpu_to_le64(armcpu->mp_affinity);
+ gicc->arm_mpidr = possible_cpus->cpus[i].arch_id;
gicc->uid = cpu_to_le32(i);
- gicc->flags = cpu_to_le32(ACPI_MADT_GICC_ENABLED);
-
- if (arm_feature(&armcpu->env, ARM_FEATURE_PMU)) {
+ if ( i < MACHINE(vms)->smp.cpus ) {
+ gicc->flags = cpu_to_le32(ACPI_MADT_GICC_ENABLED);
+ } else {
+ gicc->flags = cpu_to_le32(0);
+ }
+ if ((cpu && arm_feature(&cpu->env, ARM_FEATURE_PMU)) || vms->pmu) {
gicc->performance_interrupt = cpu_to_le32(PPI(VIRTUAL_PMU_IRQ));
}
if (vms->virt) {
--
2.30.2

View File

@ -0,0 +1,95 @@
From 22e597ca7364c7787bd9abd3da27b7aaa92f3337 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Thu, 25 Nov 2021 17:59:22 +0800
Subject: [PATCH 13/28] arm/cpuhp: Add ACPI _MAT entry for Processor object
Adds a function which builds the ACPI _MAT entry for processor objects. This
shall be called from the cpus AML for all possible vcpus.
The entry is passed to the guest kernel with ACPI_MADT_GICC_ENABLED flag when
it evaluates _MAT object. OSPM evaluates _MAT object in context to the cpu
hotplug event.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/acpi/cpu.c | 5 +++++
hw/arm/virt-acpi-build.c | 25 +++++++++++++++++++++++--
include/hw/arm/virt.h | 1 +
3 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c
index 98657ad28b..7b6765d5dd 100644
--- a/hw/acpi/cpu.c
+++ b/hw/acpi/cpu.c
@@ -691,6 +691,11 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
apic->flags = cpu_to_le32(1);
break;
}
+ case ACPI_APIC_GENERIC_CPU_INTERFACE: {
+ AcpiMadtGenericCpuInterface *gicc = (void *)madt_buf->data;
+ gicc->flags = cpu_to_le32(1);
+ break;
+ }
default:
assert(0);
}
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 416b43c9f8..48b34d50a2 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -563,6 +563,22 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
vms->oem_table_id);
}
+static void
+build_mat_entry(AcpiDeviceIf *adev, int uid, const CPUArchIdList *arch_ids,
+ GArray *entry)
+{
+ AcpiMadtGenericCpuInterface *gicc = acpi_data_push(entry,sizeof(*gicc));
+ MachineState *ms = MACHINE(qdev_get_machine());
+ CPUArchIdList *possible_cpus = ms->possible_cpus;
+
+ /* fill the relevant fields of _MAT entry for GICC */
+ gicc->type = ACPI_APIC_GENERIC_CPU_INTERFACE;
+ gicc->length = sizeof(*gicc);
+ gicc->cpu_interface_number = cpu_to_le32(uid);
+ gicc->arm_mpidr = possible_cpus->cpus[uid].arch_id;
+ gicc->uid = cpu_to_le32(uid);
+}
+
/* MADT */
static void
build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
@@ -713,8 +729,13 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
.acpi_1_compatible = false,
.has_legacy_cphp = false
};
-
- build_cpus_aml(scope, ms, opts, memmap[VIRT_CPUHP_ACPI].base,
+
+ AcpiDeviceIfClass *adevc;
+ /* _MAT entry shall be used within cpus aml */
+ adevc = ACPI_DEVICE_IF_CLASS(DEVICE_GET_CLASS(vms->acpi_dev));
+ adevc->madt_cpu = build_mat_entry;
+
+ build_cpus_aml(scope, ms, opts, memmap[VIRT_CPUHP_ACPI].base,
"\\_SB", NULL, AML_SYSTEM_MEMORY);
} else {
acpi_dsdt_add_cpus(scope, vms);
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 8954b78c27..491eeddca4 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -37,6 +37,7 @@
#include "hw/block/flash.h"
#include "sysemu/kvm.h"
#include "hw/intc/arm_gicv3_common.h"
+#include "hw/acpi/acpi_dev_interface.h"
#include "qom/object.h"
#define NUM_GICV2M_SPIS 64
--
2.30.2

View File

@ -0,0 +1,86 @@
From 435c926d8739b1ad4ffbfeabe83aabbda2d3ec22 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Wed, 1 Dec 2021 12:38:03 +0800
Subject: [PATCH 14/28] arm/cpuhp: Release objects for *disabled* possible
vcpus after init
During machvirt_init(), ARMCPU objects are pre-created along with the
corresponding KVM vcpus in the host. Disabled possible KVM vcpus are then
parked at the per-virt-machine list "kvm_parked_vcpus".
Prime purpose to pre-create ARMCPU objects for the disabled vcpus is to
facilitate the GIC initialization (pre-sized with possible vcpus). GIC
requires all vcpus corresponding to its GICC(GIC CPU Interface) to be
initialized and present during its own initialization.
After initialization of the machine is complete we release the ARMCPU objects
for the disabled vcpus(which shall be re-created at the time when vcpu is hot
plugged again. This newly created ARMCPU object is then attached with
corresponding parked KVM VCPU).
We have few options after the machine init where the disabled ARMCPU object
could be released:
1. Release in context to the virt_machine_done() notifier.(This is also our
current approach)
2. Defer the release till a new vcpu object is hot plugged. Then release the
object in context to the pre_plug() phase.
3. Never release and keep on reusing them and release once at VM exit. This
will require some modifications within the interface of qdevice_add() to
get old ARMCPU object instead of creating a new one for the hotplug request.
Each of the above approaches come with their own pros and cons. This prototype
uses the 1st approach.(suggestions are welcome!)
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 47db084183..a0b6393f76 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1505,6 +1505,28 @@ static void create_secure_ram(VirtMachineState *vms,
g_free(nodename);
}
+static void virt_remove_disabled_cpus(VirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ int n;
+
+ /*
+ * RFC: Question: Other approach could have been to keep them forever
+ * and release it only once when qemu exits as part o finalize or when
+ * new vcpu is hotplugged. In the later old could be released for the
+ * newly created object for the same vcpu?
+ */
+ for (n = MACHINE(vms)->smp.cpus; n < MACHINE(vms)->smp.max_cpus; n++) {
+ CPUState *cs = qemu_get_possible_cpu(n);
+ if (!qemu_present_cpu(cs)) {
+ CPUArchId *cpu_slot;
+ cpu_slot = virt_find_cpu_slot(ms, cs->cpu_index);
+ cpu_slot->cpu = NULL;
+ object_unref(OBJECT(cs));
+ }
+ }
+}
+
/*static bool virt_pmu_init(VirtMachineState *vms)
{
CPUArchIdList *possible_cpus = vms->parent.possible_cpus;
@@ -1608,6 +1630,9 @@ void virt_machine_done(Notifier *notifier, void *data)
virt_acpi_setup(vms);
virt_build_smbios(vms);
+
+ /* release the disabled ARMCPU objects used during init for pre-sizing */
+ virt_remove_disabled_cpus(vms);
}
static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
--
2.30.2

View File

@ -0,0 +1,97 @@
From 6a8a86ca3b4b66ab3489911bd0ad4da594531882 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Thu, 25 Nov 2021 18:15:46 +0800
Subject: [PATCH 15/28] arm/cpuhp: Update ACPI GED framework to support vcpu
hotplug
ACPI GED shall be used to convey to the guest kernel about any cpu hot-(un)plug
events. Therefore, existing ACPI GED framework inside QEMU needs to be enhanced
to support CPU hotplug state and events.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/acpi/generic_event_device.c | 42 +++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 7278b89c6a..815f4a91ce 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -238,13 +238,48 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
nvdimm_acpi_plug_cb(hotplug_dev, dev);
} else {
acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp);
- }
+ }
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
} else {
error_setg(errp, "virt: device plug request for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
}
}
+static void acpi_ged_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(hotplug_dev);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
+ } else {
+ error_setg(errp, "virt: device unplug request for the unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
+static void acpi_ged_device_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(hotplug_dev);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp);
+ } else {
+ error_setg(errp, "virt: device plug request for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
+static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
+{
+ AcpiGedState *s = ACPI_GED(adev);
+
+ acpi_cpu_ospm_status(&s->cpuhp_state, list);
+}
+
static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -284,6 +319,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
sel = ACPI_GED_PWR_DOWN_EVT;
} else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) {
sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
+ } else if (ev & ACPI_CPU_HOTPLUG_STATUS) {
+ sel = ACPI_GED_CPU_HOTPLUG_EVT;
} else {
/* Unknown event. Return without generating interrupt. */
warn_report("GED: Unsupported event %d. No irq injected", ev);
@@ -418,10 +455,13 @@ static void acpi_ged_class_init(ObjectClass *class, void *data)
dc->vmsd = &vmstate_acpi_ged;
hc->plug = acpi_ged_device_plug_cb;
+ hc->unplug_request = acpi_ged_device_unplug_request_cb;
+ hc->unplug = acpi_ged_device_unplug_cb;
hc->unplug_request = acpi_ged_unplug_request_cb;
hc->unplug = acpi_ged_unplug_cb;
adevc->send_event = acpi_ged_send_event;
+ adevc->ospm_status = acpi_ged_ospm_status;
}
static const TypeInfo acpi_ged_info = {
--
2.30.2

View File

@ -0,0 +1,171 @@
From 2010227bc3f326b0755ec3878476c2f0616ea407 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Sat, 27 Nov 2021 15:03:17 +0800
Subject: [PATCH 16/28] arm/cpuhp: Add/update basic hot-(un)plug framework
Adds the new cpu hot-unplug hooks and updates the existing hotplug hooks with
sanity checks.
Note, Functional contents of the hooks(now left with TODO comment) shall be
gradually filled in the subsequent patches in an incremental approach to patch
and logic building which would be roughly as follows:
1. (Un-)wiring of interrupts between vcpu<->gic
2. Sending events to Guest for hot-(un)plug so that guest can take appropriate
actions.
3. Notifying GIC about hot-(un)plug action so that vcpu could be (un-)stitched
to the GIC CPU interface.
4. Updating the Guest with Next boot info for this vcpu in the firmware.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 85 insertions(+), 2 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index a0b6393f76..ce34fb019a 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2668,11 +2668,22 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev,
static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
MachineState *ms = MACHINE(hotplug_dev);
ARMCPU *cpu = ARM_CPU(dev);
CPUState *cs = CPU(dev);
CPUArchId *cpu_slot;
+ if (dev->hotplugged && !vms->acpi_dev) {
+ error_setg(errp, "GED acpi device does not exists");
+ return;
+ }
+
+ if (dev->hotplugged && (vms->gic_version < VIRT_GIC_VERSION_3)) {
+ error_setg(errp, "CPU hotplug not supported with GICv%d, use GICv3 or "
+ "later", vms->gic_version);
+ return;
+ }
/* sanity check the cpu */
if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
@@ -2705,6 +2716,9 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
virt_cpu_set_properties(OBJECT(cs), cpu_slot);
+ if (dev->hotplugged) {
+ /* TODO: update GIC about this hotplug change here */
+ }
}
static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
@@ -2719,10 +2733,74 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
cpu_slot = virt_find_cpu_slot(ms, cpu->core_id);
cpu_slot->cpu = OBJECT(dev);
+ if (dev->hotplugged) {
+ /* TODO: wire the gic-cpu irqs */
+ /* TODO: update acpi hotplug state and send cpu hotplug event to guest */
+ /* TODO: register this cpu for reset & update F/W info for the next boot */
+ }
cs->disabled = false;
return;
}
+static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ CPUState *cs = CPU(dev);
+
+ if (!vms->acpi_dev || !dev->realized) {
+ error_setg(errp, "GED does not exists or device is not realized!");
+ return;
+ }
+
+ if (vms->gic_version < VIRT_GIC_VERSION_3) {
+ error_setg(errp, "CPU hot-unplug not supported with GICv%d",
+ vms->gic_version);
+ return;
+ }
+
+ if (cs->cpu_index == first_cpu->cpu_index)
+ {
+ error_setg(errp, "hot-unplug of ARM boot vcpu %d not supported",
+ first_cpu->cpu_index);
+ return;
+ }
+
+ /* TODO: request cpu hotplug from guest */
+
+ return;
+}
+
+static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ MachineState *ms = MACHINE(hotplug_dev);
+ CPUState *cs = CPU(dev);
+ CPUArchId *cpu_slot;
+
+ if (!vms->acpi_dev || !dev->realized) {
+ error_setg(errp, "GED does not exists or device is not realized!");
+ return;
+ }
+
+ cpu_slot = virt_find_cpu_slot(ms, ARM_CPU(cs)->core_id);
+
+ /* TODO: update the acpi cpu hotplug state for cpu hot-unplug */
+
+ /* TODO: unwire the gic-cpu irqs here */
+ /* TODO: update the GIC about this hot unplug change */
+
+ /* TODO: unregister this cpu for reset & update F/W info for the next boot */
+
+ qemu_opts_del(dev->opts);
+ dev->opts = NULL;
+
+ cpu_slot->cpu = NULL;
+ cs->disabled = true;
+ return;
+}
+
static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@@ -2835,9 +2913,11 @@ static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_dimm_unplug_request(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ virt_cpu_unplug_request(hotplug_dev, dev, errp);
} else {
- error_setg(errp, "device unplug request for unsupported device"
- " type: %s", object_get_typename(OBJECT(dev)));
+ error_setg(errp, "device unplug request for unsupported type: %s",
+ object_get_typename(OBJECT(dev)));
}
}
@@ -2846,6 +2926,8 @@ static void virt_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_dimm_unplug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ virt_cpu_unplug(hotplug_dev, dev, errp);
} else {
error_setg(errp, "virt: device unplug for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -2977,6 +3059,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15");
mc->get_default_cpu_node_id = virt_get_default_cpu_node_id;
mc->kvm_type = virt_kvm_type;
+ mc->has_hotpluggable_cpus = true;
assert(!mc->get_hotplug_handler);
mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
hc->pre_plug = virt_machine_device_pre_plug_cb;
--
2.30.2

View File

@ -0,0 +1,232 @@
From 8ee7755e469b3e8d1a4edb0fd703d33a163092f5 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Sat, 27 Nov 2021 15:19:39 +0800
Subject: [PATCH 17/28] arm/cpuhp: Changes to (un)wire GICC<->VCPU IRQs during
hot-(un)plug
Refactors the existing gic create code to extract common code to wire the
vcpu<->gic interrupts. This function could be used with cold-plug case and also
used when vcpu is hot-plugged. It also introduces a new function to unwire the
vcpu>->gic interrupts for the vcpu hot-unplug cases.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 144 +++++++++++++++++++++++++++++------------
hw/core/qdev.c | 2 +-
include/hw/qdev-core.h | 2 +
3 files changed, 104 insertions(+), 44 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index ce34fb019a..b0429cdf8c 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -625,6 +625,103 @@ static void create_v2m(VirtMachineState *vms)
vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
}
+static void unwire_gic_cpu_irqs(VirtMachineState *vms, CPUState *cs)
+{
+ unsigned int max_cpus = vms->max_cpus;
+ DeviceState *cpudev = DEVICE(cs);
+ DeviceState *gicdev = vms->gic;
+ int cpu = CPU(cs)->cpu_index;
+ int type = vms->gic_version;
+ int irq;
+
+ /* Mapping from the output timer irq lines from the CPU to the
+ * GIC PPI inputs we use for the virt board.
+ */
+ const int timer_irq[] = {
+ [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
+ [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
+ [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
+ [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
+ };
+
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
+ qdev_disconnect_gpio_out_named(cpudev, NULL, irq);
+ }
+
+ if (type == 3) {
+ qdev_disconnect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0);
+ } else if (vms->virt) {
+ qdev_disconnect_gpio_out_named(gicdev, SYSBUS_DEVICE_GPIO_IRQ, cpu + 4 * max_cpus);
+ }
+
+ /*
+ * RFC: Question: This currently does not takes care of intimating the devices
+ * which might be sitting on system bus. Do we need a sysbus_disconnect_irq()
+ * which also does the job of notification beside disconnection?
+ */
+ qdev_disconnect_gpio_out_named(cpudev, "pmu-interrupt", 0);
+ qdev_disconnect_gpio_out_named(gicdev, SYSBUS_DEVICE_GPIO_IRQ, cpu);
+ qdev_disconnect_gpio_out_named(gicdev,
+ SYSBUS_DEVICE_GPIO_IRQ, cpu + max_cpus);
+ qdev_disconnect_gpio_out_named(gicdev, SYSBUS_DEVICE_GPIO_IRQ,
+ cpu + 2 * max_cpus);
+ qdev_disconnect_gpio_out_named(gicdev, SYSBUS_DEVICE_GPIO_IRQ,
+ cpu + 3 * max_cpus);
+}
+
+static void wire_gic_cpu_irqs(VirtMachineState *vms, CPUState *cs)
+{
+ unsigned int max_cpus = vms->max_cpus;
+ DeviceState *cpudev = DEVICE(cs);
+ DeviceState *gicdev = vms->gic;
+ int cpu = CPU(cs)->cpu_index;
+ int type = vms->gic_version;
+ SysBusDevice *gicbusdev;
+ int ppibase;
+ int irq;
+
+ ppibase = NUM_IRQS + cpu * GIC_INTERNAL + GIC_NR_SGIS;
+
+ /* Mapping from the output timer irq lines from the CPU to the
+ * GIC PPI inputs we use for the virt board.
+ */
+ const int timer_irq[] = {
+ [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
+ [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
+ [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
+ [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
+ };
+
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
+ qdev_connect_gpio_out(cpudev, irq,
+ qdev_get_gpio_in(gicdev,
+ ppibase + timer_irq[irq]));
+ }
+
+ gicbusdev = SYS_BUS_DEVICE(gicdev);
+ if (type == 3) {
+ qemu_irq irq = qdev_get_gpio_in(gicdev,
+ ppibase + ARCH_GIC_MAINT_IRQ);
+ qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
+ 0, irq);
+ } else if (vms->virt) {
+ qemu_irq irq = qdev_get_gpio_in(gicdev,
+ ppibase + ARCH_GIC_MAINT_IRQ);
+ sysbus_connect_irq(gicbusdev, cpu + 4 * max_cpus, irq);
+ }
+
+ qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0,
+ qdev_get_gpio_in(gicdev,
+ ppibase + VIRTUAL_PMU_IRQ));
+ sysbus_connect_irq(gicbusdev, cpu, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+ sysbus_connect_irq(gicbusdev, cpu + max_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
+ sysbus_connect_irq(gicbusdev, cpu + 2 * max_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
+ sysbus_connect_irq(gicbusdev, cpu + 3 * max_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
+}
+
static void create_gic(VirtMachineState *vms)
{
MachineState *ms = MACHINE(vms);
@@ -695,47 +792,7 @@ static void create_gic(VirtMachineState *vms)
* and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs.
*/
for (i = 0; i < smp_cpus; i++) {
- DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
- int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS;
- int irq;
- /* Mapping from the output timer irq lines from the CPU to the
- * GIC PPI inputs we use for the virt board.
- */
- const int timer_irq[] = {
- [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
- [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
- [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
- [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
- };
-
- for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
- qdev_connect_gpio_out(cpudev, irq,
- qdev_get_gpio_in(vms->gic,
- ppibase + timer_irq[irq]));
- }
-
- if (type == 3) {
- qemu_irq irq = qdev_get_gpio_in(vms->gic,
- ppibase + ARCH_GIC_MAINT_IRQ);
- qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
- 0, irq);
- } else if (vms->virt) {
- qemu_irq irq = qdev_get_gpio_in(vms->gic,
- ppibase + ARCH_GIC_MAINT_IRQ);
- sysbus_connect_irq(gicbusdev, i + 4 * max_cpus, irq);
- }
-
- qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0,
- qdev_get_gpio_in(vms->gic, ppibase
- + VIRTUAL_PMU_IRQ));
-
- sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
- sysbus_connect_irq(gicbusdev, i + max_cpus,
- qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
- sysbus_connect_irq(gicbusdev, i + 2 * max_cpus,
- qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
- sysbus_connect_irq(gicbusdev, i + 3 * max_cpus,
- qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
+ wire_gic_cpu_irqs(vms, qemu_get_cpu(i));
}
fdt_add_gic_node(vms);
@@ -2724,6 +2781,7 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
MachineState *ms = MACHINE(hotplug_dev);
ARMCPU *cpu = ARM_CPU(dev);
CPUState *cs = CPU(dev);
@@ -2734,7 +2792,7 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
cpu_slot->cpu = OBJECT(dev);
if (dev->hotplugged) {
- /* TODO: wire the gic-cpu irqs */
+ wire_gic_cpu_irqs(vms, cs);
/* TODO: update acpi hotplug state and send cpu hotplug event to guest */
/* TODO: register this cpu for reset & update F/W info for the next boot */
}
@@ -2788,7 +2846,7 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
/* TODO: update the acpi cpu hotplug state for cpu hot-unplug */
- /* TODO: unwire the gic-cpu irqs here */
+ unwire_gic_cpu_irqs(vms, cs);
/* TODO: update the GIC about this hot unplug change */
/* TODO: unregister this cpu for reset & update F/W info for the next boot */
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index cefc5eaa0a..bb3dfc06da 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -552,7 +552,7 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
/* disconnect a GPIO output, returning the disconnected input (if any) */
-static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
+qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
const char *name, int n)
{
char *propname = g_strdup_printf("%s[%d]",
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index bafc311bfa..ea29e7a7af 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -553,6 +553,8 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n);
qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
const char *name, int n);
+qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
+ const char *name, int n);
BusState *qdev_get_child_bus(DeviceState *dev, const char *name);
/*** Device API. ***/
--
2.30.2

View File

@ -0,0 +1,211 @@
From f2ce0fea29008de9c95800044e0e508ba682554d Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Sat, 27 Nov 2021 16:06:48 +0800
Subject: [PATCH 18/28] arm/cpuhp: Changes to update GIC with vcpu hot-plug
notification
Adds the notification support about vcpu hot-(un)plug required to update the
GIC so that it can update its vcpu to GIC cpu interface association.
NOTE: This is using 'struct VirtMachineState' inside the notifier function.
Question: Not sure if it is right to use machine related data structure
inside GIC related files? Its design looks to be pretty much abstracted
from any machine related stuff. @Peter Maydell
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 12 +++++--
hw/intc/arm_gicv3_common.c | 54 +++++++++++++++++++++++++++++-
hw/intc/arm_gicv3_cpuif.c | 5 +++
hw/intc/gicv3_internal.h | 1 +
include/hw/arm/virt.h | 1 +
include/hw/intc/arm_gicv3_common.h | 1 +
6 files changed, 71 insertions(+), 3 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index b0429cdf8c..15595611a3 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2109,6 +2109,8 @@ static void machvirt_init(MachineState *machine)
create_fdt(vms);
+ notifier_list_init(&vms->cpuhp_notifiers);
+
possible_cpus = mc->possible_cpu_arch_ids(machine);
assert(possible_cpus->len == max_cpus);
for (n = 0; n < possible_cpus->len; n++) {
@@ -2722,6 +2724,12 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev,
dev, &error_abort);
}
+static void virt_update_gic(VirtMachineState *vms, CPUState *cs)
+{
+ /* notify gic to stitch GICC to this new cpu */
+ notifier_list_notify(&vms->cpuhp_notifiers, cs);
+}
+
static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
@@ -2774,7 +2782,7 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
}
virt_cpu_set_properties(OBJECT(cs), cpu_slot);
if (dev->hotplugged) {
- /* TODO: update GIC about this hotplug change here */
+ virt_update_gic(vms, cs);
}
}
@@ -2847,7 +2855,7 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
/* TODO: update the acpi cpu hotplug state for cpu hot-unplug */
unwire_gic_cpu_irqs(vms, cs);
- /* TODO: update the GIC about this hot unplug change */
+ virt_update_gic(vms, cs);
/* TODO: unregister this cpu for reset & update F/W info for the next boot */
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index cfc112e43e..aed9906ace 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -31,7 +31,7 @@
#include "gicv3_internal.h"
#include "hw/arm/linux-boot-if.h"
#include "sysemu/kvm.h"
-
+#include "hw/arm/virt.h"
static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs)
{
@@ -305,8 +305,57 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
}
}
+static int arm_gicv3_get_proc_num(GICv3State *s, CPUState *cpu)
+{
+ uint64_t mp_affinity;
+ uint64_t gicr_typer;
+ uint64_t cpu_affid;
+ int i;
+
+ mp_affinity = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL);
+ /* match the cpu mp-affinity to get the gic cpuif number */
+ for (i = 0; i < s->num_cpu; i++) {
+ gicr_typer = s->cpu[i].gicr_typer;
+ cpu_affid = (gicr_typer >> 32) & 0xFFFFFF;
+ if (cpu_affid == mp_affinity) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static void arm_gicv3_cpu_update_notifier(Notifier * notifier, void * data)
+{
+ VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine());
+ GICv3State *s = ARM_GICV3_COMMON(vms->gic);
+ CPUState *cpu = (CPUState *)data;
+ int gic_cpuif_num;
+
+ /* this shall get us mapped gicv3 cpuif corresponding to mpidr */
+ gic_cpuif_num = arm_gicv3_get_proc_num(s, cpu);
+ if (gic_cpuif_num < 0) {
+ error_report("Failed to associate cpu %d with any GIC cpuif",
+ cpu->cpu_index);
+ abort();
+ }
+
+ /* check if update is for vcpu hot-unplug */
+ if (qemu_present_cpu(cpu)) {
+ s->cpu[gic_cpuif_num].cpu = NULL;
+ return;
+ }
+
+ /* re-stitch the gic cpuif to this new cpu */
+ gicv3_set_gicv3state(cpu, &s->cpu[gic_cpuif_num]);
+ gicv3_set_cpustate(&s->cpu[gic_cpuif_num], cpu);
+
+ /* TODO: initialize the registers info for this newly added cpu */
+}
+
static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
{
+ VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine());
GICv3State *s = ARM_GICV3_COMMON(dev);
int i;
@@ -386,12 +435,15 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
(i << 8) |
(last << 4);
}
+ s->cpu_update_notifier.notify = arm_gicv3_cpu_update_notifier;
+ notifier_list_add(&vms->cpuhp_notifiers, &s->cpu_update_notifier);
}
static void arm_gicv3_finalize(Object *obj)
{
GICv3State *s = ARM_GICV3_COMMON(obj);
+ notifier_remove(&s->cpu_update_notifier);
g_free(s->redist_region_count);
}
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 819c032ec5..f4a0a1c2ab 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -21,6 +21,11 @@
#include "hw/irq.h"
#include "cpu.h"
+void gicv3_set_cpustate(GICv3CPUState *s, CPUState *cpu)
+{
+ s->cpu = cpu;
+}
+
void gicv3_set_gicv3state(CPUState *cpu, GICv3CPUState *s)
{
ARMCPU *arm_cpu = ARM_CPU(cpu);
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 05303a55c8..6e14a7a6cd 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -409,5 +409,6 @@ static inline void gicv3_cache_all_target_cpustates(GICv3State *s)
}
void gicv3_set_gicv3state(CPUState *cpu, GICv3CPUState *s);
+void gicv3_set_cpustate(GICv3CPUState *s, CPUState *cpu);
#endif /* QEMU_ARM_GICV3_INTERNAL_H */
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 491eeddca4..b91249201a 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -169,6 +169,7 @@ struct VirtMachineState {
DeviceState *gic;
DeviceState *acpi_dev;
Notifier powerdown_notifier;
+ NotifierList cpuhp_notifiers;
PCIBus *bus;
char *oem_id;
char *oem_table_id;
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index 91491a2f66..b36f98a413 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -248,6 +248,7 @@ struct GICv3State {
GICv3CPUState *gicd_irouter_target[GICV3_MAXIRQ];
uint32_t gicd_nsacr[DIV_ROUND_UP(GICV3_MAXIRQ, 16)];
+ Notifier cpu_update_notifier;
GICv3CPUState *cpu;
};
--
2.30.2

View File

@ -0,0 +1,268 @@
From 44cdfa821bf310d3adab8885127ff30a2c23157f Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Sat, 27 Nov 2021 17:00:11 +0800
Subject: [PATCH 19/28] arm/cpuhp: Changes required to (re)init the vcpu
register info
VCPU register info needs to be re-initialized each time vcpu is hot-plugged.
This has to be done both for emulation/TCG and KVM case. This is done in
context to the GIC update notification for any vcpu hot-(un)plug events. This
change adds that support and re-factors existing to maximize the code re-use.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/intc/arm_gicv3.c | 1 +
hw/intc/arm_gicv3_common.c | 7 +-
hw/intc/arm_gicv3_cpuif.c | 131 +++++++++++++++--------------
hw/intc/arm_gicv3_kvm.c | 7 +-
hw/intc/gicv3_internal.h | 1 +
include/hw/intc/arm_gicv3_common.h | 1 +
6 files changed, 84 insertions(+), 64 deletions(-)
diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
index d63f8af604..21a50161df 100644
--- a/hw/intc/arm_gicv3.c
+++ b/hw/intc/arm_gicv3.c
@@ -395,6 +395,7 @@ static void arm_gicv3_class_init(ObjectClass *klass, void *data)
ARMGICv3Class *agc = ARM_GICV3_CLASS(klass);
agcc->post_load = arm_gicv3_post_load;
+ agcc->init_cpu_reginfo = gicv3_init_cpu_reginfo;
device_class_set_parent_realize(dc, arm_gic_realize, &agc->parent_realize);
}
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index aed9906ace..c1307895c8 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -330,6 +330,7 @@ static void arm_gicv3_cpu_update_notifier(Notifier * notifier, void * data)
VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine());
GICv3State *s = ARM_GICV3_COMMON(vms->gic);
CPUState *cpu = (CPUState *)data;
+ ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
int gic_cpuif_num;
/* this shall get us mapped gicv3 cpuif corresponding to mpidr */
@@ -349,8 +350,10 @@ static void arm_gicv3_cpu_update_notifier(Notifier * notifier, void * data)
/* re-stitch the gic cpuif to this new cpu */
gicv3_set_gicv3state(cpu, &s->cpu[gic_cpuif_num]);
gicv3_set_cpustate(&s->cpu[gic_cpuif_num], cpu);
-
- /* TODO: initialize the registers info for this newly added cpu */
+ /* initialize the registers info for this newly added cpu */
+ if (c->init_cpu_reginfo) {
+ c->init_cpu_reginfo(cpu);
+ }
}
static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index f4a0a1c2ab..96f1637f83 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -2625,6 +2625,72 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = {
REGINFO_SENTINEL
};
+void gicv3_init_cpu_reginfo(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ GICv3CPUState *gcs = icc_cs_from_env(&cpu->env);
+
+ /* Note that we can't just use the GICv3CPUState as an opaque pointer
+ * in define_arm_cp_regs_with_opaque(), because when we're called back
+ * it might be with code translated by CPU 0 but run by CPU 1, in
+ * which case we'd get the wrong value.
+ * So instead we define the regs with no ri->opaque info, and
+ * get back to the GICv3CPUState from the CPUARMState.
+ */
+ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo);
+ if (arm_feature(&cpu->env, ARM_FEATURE_EL2)
+ && cpu->gic_num_lrs) {
+ int j;
+
+ gcs->num_list_regs = cpu->gic_num_lrs;
+ gcs->vpribits = cpu->gic_vpribits;
+ gcs->vprebits = cpu->gic_vprebits;
+
+ /* Check against architectural constraints: getting these
+ * wrong would be a bug in the CPU code defining these,
+ * and the implementation relies on them holding.
+ */
+ g_assert(gcs->vprebits <= gcs->vpribits);
+ g_assert(gcs->vprebits >= 5 && gcs->vprebits <= 7);
+ g_assert(gcs->vpribits >= 5 && gcs->vpribits <= 8);
+
+ define_arm_cp_regs(cpu, gicv3_cpuif_hcr_reginfo);
+
+ for (j = 0; j < gcs->num_list_regs; j++) {
+ /* Note that the AArch64 LRs are 64-bit; the AArch32 LRs
+ * are split into two cp15 regs, LR (the low part, with the
+ * same encoding as the AArch64 LR) and LRC (the high part).
+ */
+ ARMCPRegInfo lr_regset[] = {
+ { .name = "ICH_LRn_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 12,
+ .crm = 12 + (j >> 3), .opc2 = j & 7,
+ .type = ARM_CP_IO | ARM_CP_NO_RAW,
+ .access = PL2_RW,
+ .readfn = ich_lr_read,
+ .writefn = ich_lr_write,
+ },
+ { .name = "ICH_LRCn_EL2", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 4, .crn = 12,
+ .crm = 14 + (j >> 3), .opc2 = j & 7,
+ .type = ARM_CP_IO | ARM_CP_NO_RAW,
+ .access = PL2_RW,
+ .readfn = ich_lr_read,
+ .writefn = ich_lr_write,
+ },
+ REGINFO_SENTINEL
+ };
+ define_arm_cp_regs(cpu, lr_regset);
+ }
+ if (gcs->vprebits >= 6) {
+ define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr1_reginfo);
+ }
+ if (gcs->vprebits == 7) {
+ define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo);
+ }
+ }
+}
+
static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque)
{
GICv3CPUState *cs = opaque;
@@ -2641,67 +2707,10 @@ void gicv3_init_cpuif(GICv3State *s)
for (i = 0; i < s->num_cpu; i++) {
ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i));
- GICv3CPUState *cs = &s->cpu[i];
-
- /* Note that we can't just use the GICv3CPUState as an opaque pointer
- * in define_arm_cp_regs_with_opaque(), because when we're called back
- * it might be with code translated by CPU 0 but run by CPU 1, in
- * which case we'd get the wrong value.
- * So instead we define the regs with no ri->opaque info, and
- * get back to the GICv3CPUState from the CPUARMState.
- */
- define_arm_cp_regs(cpu, gicv3_cpuif_reginfo);
- if (arm_feature(&cpu->env, ARM_FEATURE_EL2)
- && cpu->gic_num_lrs) {
- int j;
-
- cs->num_list_regs = cpu->gic_num_lrs;
- cs->vpribits = cpu->gic_vpribits;
- cs->vprebits = cpu->gic_vprebits;
-
- /* Check against architectural constraints: getting these
- * wrong would be a bug in the CPU code defining these,
- * and the implementation relies on them holding.
- */
- g_assert(cs->vprebits <= cs->vpribits);
- g_assert(cs->vprebits >= 5 && cs->vprebits <= 7);
- g_assert(cs->vpribits >= 5 && cs->vpribits <= 8);
-
- define_arm_cp_regs(cpu, gicv3_cpuif_hcr_reginfo);
-
- for (j = 0; j < cs->num_list_regs; j++) {
- /* Note that the AArch64 LRs are 64-bit; the AArch32 LRs
- * are split into two cp15 regs, LR (the low part, with the
- * same encoding as the AArch64 LR) and LRC (the high part).
- */
- ARMCPRegInfo lr_regset[] = {
- { .name = "ICH_LRn_EL2", .state = ARM_CP_STATE_BOTH,
- .opc0 = 3, .opc1 = 4, .crn = 12,
- .crm = 12 + (j >> 3), .opc2 = j & 7,
- .type = ARM_CP_IO | ARM_CP_NO_RAW,
- .access = PL2_RW,
- .readfn = ich_lr_read,
- .writefn = ich_lr_write,
- },
- { .name = "ICH_LRCn_EL2", .state = ARM_CP_STATE_AA32,
- .cp = 15, .opc1 = 4, .crn = 12,
- .crm = 14 + (j >> 3), .opc2 = j & 7,
- .type = ARM_CP_IO | ARM_CP_NO_RAW,
- .access = PL2_RW,
- .readfn = ich_lr_read,
- .writefn = ich_lr_write,
- },
- REGINFO_SENTINEL
- };
- define_arm_cp_regs(cpu, lr_regset);
- }
- if (cs->vprebits >= 6) {
- define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr1_reginfo);
- }
- if (cs->vprebits == 7) {
- define_arm_cp_regs(cpu, gicv3_cpuif_ich_apxr23_reginfo);
- }
+ if (qemu_present_cpu(CPU(cpu))) {
+ GICv3CPUState *cs = icc_cs_from_env(&cpu->env);
+ gicv3_init_cpu_reginfo(CPU(cpu));
+ arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs);
}
- arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs);
}
}
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 4e7bb4ac1f..65ac3d5a88 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -789,6 +789,10 @@ static void vm_change_state_handler(void *opaque, bool running,
}
}
+static void kvm_gicv3_init_cpu_reginfo(CPUState *cs)
+{
+ define_arm_cp_regs(ARM_CPU(cs), gicv3_cpuif_reginfo);
+}
static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
{
@@ -821,7 +825,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
for (i = 0; i < s->num_cpu; i++) {
CPUState *cs = qemu_get_cpu(i);
if (qemu_present_cpu(cs))
- define_arm_cp_regs(ARM_CPU(cs), gicv3_cpuif_reginfo);
+ kvm_gicv3_init_cpu_reginfo(cs);
}
/* Try to create the device via the device control API */
@@ -908,6 +912,7 @@ static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data)
agcc->pre_save = kvm_arm_gicv3_get;
agcc->post_load = kvm_arm_gicv3_put;
+ agcc->init_cpu_reginfo = kvm_gicv3_init_cpu_reginfo;
device_class_set_parent_realize(dc, kvm_arm_gicv3_realize,
&kgc->parent_realize);
device_class_set_parent_reset(dc, kvm_arm_gicv3_reset, &kgc->parent_reset);
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 6e14a7a6cd..66eec3c3fc 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -298,6 +298,7 @@ void gicv3_dist_set_irq(GICv3State *s, int irq, int level);
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level);
void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
void gicv3_init_cpuif(GICv3State *s);
+void gicv3_init_cpu_reginfo(CPUState *cs);
/**
* gicv3_cpuif_update:
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index b36f98a413..b2457cbcb8 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -291,6 +291,7 @@ struct ARMGICv3CommonClass {
void (*pre_save)(GICv3State *s);
void (*post_load)(GICv3State *s);
+ void (*init_cpu_reginfo)(CPUState *cs);
};
void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
--
2.30.2

View File

@ -0,0 +1,115 @@
From 740d5e7968376d6007fcc71008f277b8ea94a2fb Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Sat, 27 Nov 2021 17:07:55 +0800
Subject: [PATCH 20/28] arm/cpuhp: Update the guest(via GED) about cpu
hot-(un)plug events
During any vcpu hot-(un)plug, running guest VM needs to be intimated about the
new vcpu being added or request the deletion of the vcpu which is already part
of the guest VM. This is done using the ACPI GED event which eventually gets
demultiplexed to a CPU hotplug event and further to specific hot-(un)plug event
of a particular vcpu.
This change adds the ACPI calls to the existing hot-(un)plug hooks to trigger
ACPI GED events from QEMU to guest VM.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 31 +++++++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 15595611a3..c3073d6755 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2794,17 +2794,25 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
ARMCPU *cpu = ARM_CPU(dev);
CPUState *cs = CPU(dev);
CPUArchId *cpu_slot;
+ Error *local_err = NULL;
/* insert the cold/hot-plugged vcpu in the slot */
cpu_slot = virt_find_cpu_slot(ms, cpu->core_id);
cpu_slot->cpu = OBJECT(dev);
if (dev->hotplugged) {
+ HotplugHandlerClass *hhc;
wire_gic_cpu_irqs(vms, cs);
- /* TODO: update acpi hotplug state and send cpu hotplug event to guest */
+ /* update acpi hotplug state and send cpu hotplug event to guest */
+ hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
+ hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err);
+ if (local_err)
+ goto fail;
/* TODO: register this cpu for reset & update F/W info for the next boot */
}
cs->disabled = false;
+fail:
+ error_propagate(errp, local_err);
return;
}
@@ -2812,7 +2820,9 @@ static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ HotplugHandlerClass *hhc;
CPUState *cs = CPU(dev);
+ Error *local_err = NULL;
if (!vms->acpi_dev || !dev->realized) {
error_setg(errp, "GED does not exists or device is not realized!");
@@ -2832,8 +2842,13 @@ static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev,
return;
}
- /* TODO: request cpu hotplug from guest */
-
+ /* request cpu hotplug from guest */
+ hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
+ hhc->unplug_request(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err);
+ if (local_err)
+ goto fail;
+fail:
+ error_propagate(errp, local_err);
return;
}
@@ -2842,8 +2857,10 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
{
VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
MachineState *ms = MACHINE(hotplug_dev);
+ HotplugHandlerClass *hhc;
CPUState *cs = CPU(dev);
CPUArchId *cpu_slot;
+ Error *local_err = NULL;
if (!vms->acpi_dev || !dev->realized) {
error_setg(errp, "GED does not exists or device is not realized!");
@@ -2852,7 +2869,11 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
cpu_slot = virt_find_cpu_slot(ms, ARM_CPU(cs)->core_id);
- /* TODO: update the acpi cpu hotplug state for cpu hot-unplug */
+ /* update the acpi cpu hotplug state for cpu hot-unplug */
+ hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
+ hhc->unplug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err);
+ if (local_err)
+ goto fail;
unwire_gic_cpu_irqs(vms, cs);
virt_update_gic(vms, cs);
@@ -2865,6 +2886,8 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
cpu_slot->cpu = NULL;
cs->disabled = true;
return;
+fail:
+ error_propagate(errp, local_err);
}
static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
--
2.30.2

View File

@ -0,0 +1,109 @@
From e9301ff546d27cf5f6acc677d19e7c89c693d6ea Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Sat, 27 Nov 2021 17:14:45 +0800
Subject: [PATCH 21/28] arm/cpuhp: Changes required for reset and to support
next boot
Updates the firmware config with the next boot cpus information and also
registers the reset callback to be called when guest reboots to reset the cpu.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/boot.c | 2 +-
hw/arm/virt.c | 17 +++++++++++++----
include/hw/arm/boot.h | 2 ++
include/hw/arm/virt.h | 1 +
4 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 57efb61ee4..e966d8e032 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -675,7 +675,7 @@ fail:
return -1;
}
-static void do_cpu_reset(void *opaque)
+void do_cpu_reset(void *opaque)
{
ARMCPU *cpu = opaque;
CPUState *cs = CPU(cpu);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c3073d6755..ab35bd51af 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -48,6 +48,7 @@
#include "sysemu/numa.h"
#include "sysemu/runstate.h"
#include "sysemu/tpm.h"
+#include "sysemu/reset.h"
#include "sysemu/kvm.h"
#include "hw/loader.h"
#include "qapi/error.h"
@@ -1236,7 +1237,7 @@ static FWCfgState *create_fw_cfg(const VirtMachineState *vms, AddressSpace *as)
char *nodename;
fw_cfg = fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, as);
- fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)ms->smp.cpus);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, vms->boot_cpus);
nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
@@ -2808,7 +2809,12 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err);
if (local_err)
goto fail;
- /* TODO: register this cpu for reset & update F/W info for the next boot */
+ /* register this cpu for reset & update F/W info for the next boot */
+ qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
+ vms->boot_cpus++;
+ if (vms->fw_cfg) {
+ fw_cfg_modify_i16(vms->fw_cfg, FW_CFG_NB_CPUS, vms->boot_cpus);
+ }
}
cs->disabled = false;
fail:
@@ -2878,8 +2884,11 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
unwire_gic_cpu_irqs(vms, cs);
virt_update_gic(vms, cs);
- /* TODO: unregister this cpu for reset & update F/W info for the next boot */
-
+ qemu_unregister_reset(do_cpu_reset, ARM_CPU(cs));
+ vms->boot_cpus--;
+ if (vms->fw_cfg) {
+ fw_cfg_modify_i16(vms->fw_cfg, FW_CFG_NB_CPUS, vms->boot_cpus);
+ }
qemu_opts_del(dev->opts);
dev->opts = NULL;
diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h
index ce2b48b88b..aa156967af 100644
--- a/include/hw/arm/boot.h
+++ b/include/hw/arm/boot.h
@@ -163,6 +163,8 @@ AddressSpace *arm_boot_address_space(ARMCPU *cpu,
int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
hwaddr addr_limit, AddressSpace *as, MachineState *ms);
+void do_cpu_reset(void *opaque);
+
/* Write a secure board setup routine with a dummy handler for SMCs */
void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
const struct arm_boot_info *info,
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index b91249201a..f18d797245 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -160,6 +160,7 @@ struct VirtMachineState {
const int *irqmap;
int fdt_size;
int max_cpus;
+ uint16_t boot_cpus;
uint32_t clock_phandle;
uint32_t gic_phandle;
uint32_t msi_phandle;
--
2.30.2

View File

@ -0,0 +1,415 @@
From 71102726bd7434d8fd635be0f1c067fdb795efe3 Mon Sep 17 00:00:00 2001
From: Salil Mehta <salil.mehta@huawei.com>
Date: Sat, 27 Nov 2021 17:37:22 +0800
Subject: [PATCH 22/28] arm/cpuhp: Add support of *unrealize* ARMCPU during
vcpu hot-unplug
During vcpu hot-unplug ARM cpu unrealization shall happen which should do away
with all the vcpu thread creations, allocations, registrations which happened
as part of the realization process of the ARM cpu. This change introduces the
ARM cpu unrealize function taking care of exactly that.
Note, initialized vcpus are not destroyed at host KVM but are rather parked in
the QEMU/KVM layer. These are later reused once vcpu is hotplugged again.
Co-developed-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
gdbstub.c | 13 +++++++
include/exec/exec-all.h | 8 ++++
include/exec/gdbstub.h | 1 +
include/hw/core/cpu.h | 2 +
softmmu/physmem.c | 24 ++++++++++++
target/arm/cpu-qom.h | 3 ++
target/arm/cpu.c | 86 +++++++++++++++++++++++++++++++++++++++++
target/arm/cpu.h | 14 +++++++
target/arm/helper.c | 31 +++++++++++++++
target/arm/internals.h | 1 +
target/arm/kvm64.c | 6 ++-
11 files changed, 188 insertions(+), 1 deletion(-)
diff --git a/gdbstub.c b/gdbstub.c
index 52bde5bdc9..d5fb3cb9ae 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1005,6 +1005,19 @@ void gdb_register_coprocessor(CPUState *cpu,
}
}
+void gdb_unregister_coprocessor_all(CPUState *cpu)
+{
+ GDBRegisterState *s, *p;
+
+ p = cpu->gdb_regs;
+ while (p) {
+ s = p;
+ p = p->next;
+ g_free(s);
+ }
+ cpu->gdb_regs = NULL;
+}
+
#ifndef CONFIG_USER_ONLY
/* Translate GDB watchpoint type to a flags value for cpu_watchpoint_* */
static inline int xlat_gdb_type(CPUState *cpu, int gdbtype)
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 5d1b6d80fb..1fbe9aee0c 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -106,6 +106,14 @@ void cpu_reloading_memory_map(void);
*/
void cpu_address_space_init(CPUState *cpu, int asidx,
const char *prefix, MemoryRegion *mr);
+/**
+ * cpu_address_space_destroy:
+ * @cpu: CPU for which address space needs to be destroyed
+ * @asidx: integer index of this address space
+ *
+ * Note that with KVM only one address space is supported.
+ */
+void cpu_address_space_destroy(CPUState *cpu, int asidx);
#endif
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG)
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index a024a0350d..1a2100d014 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -84,6 +84,7 @@ void gdb_register_coprocessor(CPUState *cpu,
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
int num_regs, const char *xml, int g_pos);
+void gdb_unregister_coprocessor_all(CPUState *cpu);
/*
* The GDB remote protocol transfers values in target byte order. As
* the gdbstub may be batching up several register values we always
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index 5a2571af3e..e50c13f889 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -344,6 +344,7 @@ struct CPUState {
QSIMPLEQ_HEAD(, qemu_work_item) work_list;
CPUAddressSpace *cpu_ases;
+ int cpu_ases_ref_count;
int num_ases;
AddressSpace *as;
MemoryRegion *memory;
@@ -376,6 +377,7 @@ struct CPUState {
int kvm_fd;
struct KVMState *kvm_state;
struct kvm_run *kvm_run;
+ VMChangeStateEntry *vmcse;
struct kvm_dirty_gfn *kvm_dirty_gfns;
uint32_t kvm_fetch_index;
diff --git a/softmmu/physmem.c b/softmmu/physmem.c
index 2e18947598..75a50fa1b7 100644
--- a/softmmu/physmem.c
+++ b/softmmu/physmem.c
@@ -748,6 +748,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
if (!cpu->cpu_ases) {
cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases);
+ cpu->cpu_ases_ref_count = cpu->num_ases;
}
newas = &cpu->cpu_ases[asidx];
@@ -760,6 +761,29 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
}
}
+void cpu_address_space_destroy(CPUState *cpu, int asidx)
+{
+ CPUAddressSpace *cpuas;
+
+ assert(asidx < cpu->num_ases);
+ assert(asidx == 0 || !kvm_enabled());
+ assert(cpu->cpu_ases);
+
+ cpuas = &cpu->cpu_ases[asidx];
+ if (tcg_enabled()) {
+ memory_listener_unregister(&cpuas->tcg_as_listener);
+ }
+
+ address_space_destroy(cpuas->as);
+
+ if(cpu->cpu_ases_ref_count == 1) {
+ g_free(cpu->cpu_ases);
+ cpu->cpu_ases = NULL;
+ }
+
+ cpu->cpu_ases_ref_count--;
+}
+
AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx)
{
/* Return the AddressSpace corresponding to the specified index */
diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h
index a22bd506d0..ef83507121 100644
--- a/target/arm/cpu-qom.h
+++ b/target/arm/cpu-qom.h
@@ -55,6 +55,7 @@ struct ARMCPUClass {
const ARMCPUInfo *info;
DeviceRealize parent_realize;
+ DeviceUnrealize parent_unrealize;
DeviceReset parent_reset;
};
@@ -71,7 +72,9 @@ struct AArch64CPUClass {
};
void register_cp_regs_for_features(ARMCPU *cpu);
+void unregister_cp_regs_for_features(ARMCPU *cpu);
void init_cpreg_list(ARMCPU *cpu);
+void destroy_cpreg_list(ARMCPU *cpu);
/* Callback functions for the generic timer's timers. */
void arm_gt_ptimer_cb(void *opaque);
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index ff827d56b7..455ad5aa9e 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -97,6 +97,16 @@ void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
QLIST_INSERT_HEAD(&cpu->pre_el_change_hooks, entry, node);
}
+void arm_unregister_pre_el_change_hooks(ARMCPU *cpu)
+{
+ ARMELChangeHook *entry, *next;
+
+ QLIST_FOREACH_SAFE(entry, &cpu->pre_el_change_hooks, node, next) {
+ QLIST_REMOVE(entry, node);
+ g_free(entry);
+ }
+}
+
void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
void *opaque)
{
@@ -108,6 +118,16 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node);
}
+void arm_unregister_el_change_hooks(ARMCPU *cpu)
+{
+ ARMELChangeHook *entry, *next;
+
+ QLIST_FOREACH_SAFE(entry, &cpu->el_change_hooks, node, next) {
+ QLIST_REMOVE(entry, node);
+ g_free(entry);
+ }
+}
+
static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
{
/* Reset a single ARMCPRegInfo register */
@@ -139,6 +159,70 @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
}
}
+static void arm_cpu_unrealizefn(DeviceState *dev)
+{
+ ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev);
+ ARMCPU *cpu = ARM_CPU(dev);
+ CPUARMState *env = &cpu->env;
+ CPUState *cs = CPU(dev);
+
+ /* rock 'n' un-roll, whatever happened in the arm_cpu_realizefn cleanly */
+ if (cpu->has_el3 || arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ cpu_address_space_destroy(cs, ARMASIdx_S);
+ }
+ cpu_address_space_destroy(cs, ARMASIdx_NS);
+
+ destroy_cpreg_list(cpu);
+ arm_cpu_unregister_gdb_regs(cpu);
+ unregister_cp_regs_for_features(cpu);
+
+ if (cpu->sau_sregion && arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ g_free(env->sau.rbar);
+ g_free(env->sau.rlar);
+ }
+
+ if (arm_feature(env, ARM_FEATURE_PMSA) &&
+ arm_feature(env, ARM_FEATURE_V7) &&
+ cpu->pmsav7_dregion) {
+ if (arm_feature(env, ARM_FEATURE_V8)) {
+ g_free(env->pmsav8.rbar[M_REG_NS]);
+ g_free(env->pmsav8.rlar[M_REG_NS]);
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ g_free(env->pmsav8.rbar[M_REG_S]);
+ g_free(env->pmsav8.rlar[M_REG_S]);
+ }
+ } else {
+ g_free(env->pmsav7.drbar);
+ g_free(env->pmsav7.drsr);
+ g_free(env->pmsav7.dracr);
+ }
+ }
+
+ if (arm_feature(env, ARM_FEATURE_PMU)) {
+ if (!kvm_enabled()) {
+ arm_unregister_pre_el_change_hooks(cpu);
+ arm_unregister_el_change_hooks(cpu);
+ }
+
+#ifndef CONFIG_USER_ONLY
+ if (cpu->pmu_timer) {
+ timer_del(cpu->pmu_timer);
+ }
+#endif
+ }
+
+ cpu_remove_sync(CPU(dev));
+ acc->parent_unrealize(dev);
+
+#ifndef CONFIG_USER_ONLY
+ timer_del(cpu->gt_timer[GTIMER_PHYS]);
+ timer_del(cpu->gt_timer[GTIMER_VIRT]);
+ timer_del(cpu->gt_timer[GTIMER_HYP]);
+ timer_del(cpu->gt_timer[GTIMER_SEC]);
+ timer_del(cpu->gt_timer[GTIMER_HYPVIRT]);
+#endif
+}
+
static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque)
{
/* Purely an assertion check: we've already done reset once,
@@ -2021,6 +2105,8 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
device_class_set_props(dc, arm_cpu_properties);
device_class_set_parent_reset(dc, arm_cpu_reset, &acc->parent_reset);
+ device_class_set_parent_unrealize(dc, arm_cpu_unrealizefn,
+ &acc->parent_unrealize);
cc->class_by_name = arm_cpu_class_by_name;
cc->has_work = arm_cpu_has_work;
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index ba11468ab5..f7f3308c42 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1144,6 +1144,13 @@ void arm_pmu_timer_cb(void *opaque);
* Functions to register as EL change hooks for PMU mode filtering
*/
void pmu_pre_el_change(ARMCPU *cpu, void *ignored);
+
+/**
+ * arm_unregister_pre_el_change_hook:
+ * unregister all pre EL change hook functions. Generally called during
+ * unrealize'ing leg
+ */
+void arm_unregister_pre_el_change_hooks(ARMCPU *cpu);
void pmu_post_el_change(ARMCPU *cpu, void *ignored);
/*
@@ -3616,6 +3623,13 @@ void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void
*opaque);
+/**
+ * arm_unregister_el_change_hook:
+ * unregister all EL change hook functions. Generally called during
+ * unrealize'ing leg
+ */
+void arm_unregister_el_change_hooks(ARMCPU *cpu);
+
/**
* arm_rebuild_hflags:
* Rebuild the cached TBFLAGS for arbitrary changed processor state.
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 155d8bf239..3c61b16b56 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -507,6 +507,19 @@ void init_cpreg_list(ARMCPU *cpu)
g_list_free(keys);
}
+void destroy_cpreg_list(ARMCPU *cpu)
+{
+ assert(cpu->cpreg_indexes);
+ assert(cpu->cpreg_values);
+ assert(cpu->cpreg_vmstate_indexes);
+ assert(cpu->cpreg_vmstate_values);
+
+ g_free(cpu->cpreg_indexes);
+ g_free(cpu->cpreg_values);
+ g_free(cpu->cpreg_vmstate_indexes);
+ g_free(cpu->cpreg_vmstate_values);
+}
+
/*
* Some registers are not accessible from AArch32 EL3 if SCR.NS == 0.
*/
@@ -8671,6 +8684,18 @@ void register_cp_regs_for_features(ARMCPU *cpu)
#endif
}
+void unregister_cp_regs_for_features(ARMCPU *cpu)
+{
+ CPUARMState *env = &cpu->env;
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ /* M profile has no coprocessor registers */
+ return;
+ }
+
+ /* empty it all. unregister all the coprocessor registers */
+ g_hash_table_remove_all(cpu->cp_regs);
+}
+
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
{
CPUState *cs = CPU(cpu);
@@ -8709,6 +8734,12 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
}
+void arm_cpu_unregister_gdb_regs(ARMCPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+ gdb_unregister_coprocessor_all(cs);
+}
+
/* Sort alphabetically by type name, except for "any". */
static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b)
{
diff --git a/target/arm/internals.h b/target/arm/internals.h
index cd2ea8a388..fbdc3f2eab 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -173,6 +173,7 @@ static inline int r14_bank_number(int mode)
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu);
void arm_translate_init(void);
+void arm_cpu_unregister_gdb_regs(ARMCPU *cpu);
#ifdef CONFIG_TCG
void arm_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb);
#endif /* CONFIG_TCG */
diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
index 59982d470d..19d3eac253 100644
--- a/target/arm/kvm64.c
+++ b/target/arm/kvm64.c
@@ -839,7 +839,9 @@ int kvm_arch_init_vcpu(CPUState *cs)
return -EINVAL;
}
- qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cs);
+ if (qemu_present_cpu(cs))
+ cs->vmcse = qemu_add_vm_change_state_handler(kvm_arm_vm_state_change,
+ cs);
/* Determine init features for this CPU */
memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features));
@@ -904,6 +906,8 @@ int kvm_arch_init_vcpu(CPUState *cs)
int kvm_arch_destroy_vcpu(CPUState *cs)
{
+ if (qemu_present_cpu(cs))
+ qemu_del_vm_change_state_handler(cs->vmcse);
return 0;
}
--
2.30.2

View File

@ -0,0 +1,38 @@
From 5864b2046f2481772bb1f28aa4e4bbc5258ad1f1 Mon Sep 17 00:00:00 2001
From: Huang Shijie <shijie8@gmail.com>
Date: Wed, 8 Dec 2021 13:35:28 +0800
Subject: [PATCH 23/28] armcpuhp: initial pvtime and pmu for all possible cpu
initial pvtime and pmu for all possible cpus when machvirt init.
Signed-off-by: Huang Shijie <shijie8@gmail.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index ab35bd51af..3f4763367a 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1933,6 +1933,7 @@ static void virt_cpu_set_properties(Object *cpuobj, const CPUArchId *cpu_slot)
*/
static void virt_cpu_post_init(VirtMachineState *vms, MemoryRegion *sysmem)
{
+ CPUArchIdList *possible_cpus = vms->parent.possible_cpus;
int max_cpus = MACHINE(vms)->smp.max_cpus;
bool aarch64, pmu, steal_time;
CPUState *cpu;
@@ -1965,7 +1966,8 @@ static void virt_cpu_post_init(VirtMachineState *vms, MemoryRegion *sysmem)
memory_region_add_subregion(sysmem, pvtime_reg_base, pvtime);
}
- CPU_FOREACH(cpu) {
+ for (int n = 0; n < possible_cpus->len; n++) {
+ cpu = qemu_get_possible_cpu(n);
if (pmu) {
assert(arm_feature(&ARM_CPU(cpu)->env, ARM_FEATURE_PMU));
if (kvm_irqchip_in_kernel()) {
--
2.30.2

View File

@ -0,0 +1,47 @@
From b165fbe697c6f029973fffea151e384247639829 Mon Sep 17 00:00:00 2001
From: Huang Shijie <shijie8@gmail.com>
Date: Wed, 8 Dec 2021 16:15:33 +0800
Subject: [PATCH 24/28] armcpuhp: add cpu hotunplug frame work in acpi device
handle
Signed-off-by: Huang Shijie <shijie8@gmail.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/acpi/generic_event_device.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 815f4a91ce..22a3fb348b 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -12,7 +12,9 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/acpi/acpi.h"
+#include "hw/acpi/cpu.h"
#include "hw/acpi/generic_event_device.h"
+#include "hw/arm/virt.h"
#include "hw/irq.h"
#include "hw/mem/pc-dimm.h"
#include "hw/mem/nvdimm.h"
@@ -288,6 +290,8 @@ static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev,
if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
!(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) {
acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
} else {
error_setg(errp, "acpi: device unplug request for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -301,6 +305,8 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
acpi_memory_unplug_cb(&s->memhp_state, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp);
} else {
error_setg(errp, "acpi: device unplug for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
--
2.30.2

View File

@ -0,0 +1,180 @@
From ee68b08a7194ea9fefdf00ee57fa9edcb56b4359 Mon Sep 17 00:00:00 2001
From: Huang Shijie <shijie8@gmail.com>
Date: Wed, 8 Dec 2021 17:08:07 +0800
Subject: [PATCH 25/28] cleanup code of arm cpu hotplug support
Signed-off-by: Huang Shijie <shijie8@gmail.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 136 +-------------------------------------------------
1 file changed, 1 insertion(+), 135 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 3f4763367a..5c04abb352 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1585,38 +1585,6 @@ static void virt_remove_disabled_cpus(VirtMachineState *vms)
}
}
-/*static bool virt_pmu_init(VirtMachineState *vms)
-{
- CPUArchIdList *possible_cpus = vms->parent.possible_cpus;
- ARMCPU *armcpu;
- int n; */
-
- /*
- * As of now KVM ensures that within the host all the vcpus have same
- * features configured. This cannot be changed later and cannot be diferent
- * for new vcpus being plugged in. Also, -cpu option/virt machine cpu-type
- * ensures all the vcpus are identical.
- */
-/* for (n = 0; n < possible_cpus->len; n++) {
- CPUState *cpu = qemu_get_possible_cpu(n);
- armcpu = ARM_CPU(cpu);
-
- if (!arm_feature(&armcpu->env, ARM_FEATURE_PMU)) {
- warn_report("Not all vcpus might have PMU initialized");
- return false;
- }
-
- if (kvm_enabled()) {
- if (kvm_irqchip_in_kernel()) {
- kvm_arm_pmu_set_irq(cpu, PPI(VIRTUAL_PMU_IRQ));
- }
- kvm_arm_pmu_init(cpu);
- }
- }
-
- return true;
-}*/
-
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
{
const VirtMachineState *board = container_of(binfo, VirtMachineState,
@@ -2120,110 +2088,11 @@ static void machvirt_init(MachineState *machine)
Object *cpuobj;
CPUState *cs;
-/* if (n >= smp_cpus) {
- break;
- }
-*/
cpuobj = object_new(possible_cpus->cpus[n].type);
cs = CPU(cpuobj);
-/* object_property_set_int(cpuobj, "mp-affinity",
- possible_cpus->cpus[n].arch_id, NULL);
-
- cs = CPU(cpuobj);
- cs->cpu_index = n;
-
- numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj),
- &error_fatal);
-*/
aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL);
object_property_set_int(cpuobj, "core-id", n, NULL);
-/* if (!vms->secure) {
- object_property_set_bool(cpuobj, "has_el3", false, NULL);
- }
-
- if (!vms->virt && object_property_find(cpuobj, "has_el2")) {
- object_property_set_bool(cpuobj, "has_el2", false, NULL);
- }
-
- if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) {
- object_property_set_int(cpuobj, "psci-conduit", vms->psci_conduit,
- NULL);
-*/
- /* Secondary CPUs start in PSCI powered-down state */
-/* if (n > 0) {
- object_property_set_bool(cpuobj, "start-powered-off", true,
- NULL);
- }
- }
-
- if (vmc->kvm_no_adjvtime &&
- object_property_find(cpuobj, "kvm-no-adjvtime")) {
- object_property_set_bool(cpuobj, "kvm-no-adjvtime", true, NULL);
- }
-
- if (vmc->no_kvm_steal_time &&
- object_property_find(cpuobj, "kvm-steal-time")) {
- object_property_set_bool(cpuobj, "kvm-steal-time", false, NULL);
- }
-
- if (vmc->no_pmu && object_property_find(cpuobj, "pmu")) {
- object_property_set_bool(cpuobj, "pmu", false, NULL);
- }
-
- if (object_property_find(cpuobj, "reset-cbar")) {
- object_property_set_int(cpuobj, "reset-cbar",
- vms->memmap[VIRT_CPUPERIPHS].base,
- &error_abort);
- }
-
- object_property_set_link(cpuobj, "memory", OBJECT(sysmem),
- &error_abort);
- if (vms->secure) {
- object_property_set_link(cpuobj, "secure-memory",
- OBJECT(secure_sysmem), &error_abort);
- }
-*/
-// if (vms->mte) {
- /* Create the memory region only once, but link to all cpus. */
-// if (!tag_sysmem) {
- /*
- * The property exists only if MemTag is supported.
- * If it is, we must allocate the ram to back that up.
- */
-/* if (!object_property_find(cpuobj, "tag-memory")) {
- error_report("MTE requested, but not supported "
- "by the guest CPU");
- exit(1);
- }
-
- tag_sysmem = g_new(MemoryRegion, 1);
- memory_region_init(tag_sysmem, OBJECT(machine),
- "tag-memory", UINT64_MAX / 32);
-
- if (vms->secure) {
- secure_tag_sysmem = g_new(MemoryRegion, 1);
- memory_region_init(secure_tag_sysmem, OBJECT(machine),
- "secure-tag-memory", UINT64_MAX / 32);
-*/
- /* As with ram, secure-tag takes precedence over tag. */
-/* memory_region_add_subregion_overlap(secure_tag_sysmem, 0,
- tag_sysmem, -1);
- }
- }
-
- object_property_set_link(cpuobj, "tag-memory", OBJECT(tag_sysmem),
- &error_abort);
- if (vms->secure) {
- object_property_set_link(cpuobj, "secure-tag-memory",
- OBJECT(secure_tag_sysmem),
- &error_abort);
- }
- }
-
- qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);
- object_unref(cpuobj);
-*/
if (n < smp_cpus) {
char *core_id = g_strdup_printf("core%d", n);
qdev_set_id(DEVICE(cpuobj),core_id);
@@ -2283,10 +2152,7 @@ static void machvirt_init(MachineState *machine)
virt_cpu_post_init(vms, sysmem);
-// if (!vmc->no_pmu && virt_pmu_init(vms)) {
-// vms->pmu = true;
- fdt_add_pmu_nodes(vms);
-// }
+ fdt_add_pmu_nodes(vms);
create_uart(vms, VIRT_UART, sysmem, serial_hd(0));
if (vms->secure) {
--
2.30.2

View File

@ -0,0 +1,65 @@
From 0e3ad502669659822083fd9fde80220e5a9af0ed Mon Sep 17 00:00:00 2001
From: Huang Shijie <shijie8@gmail.com>
Date: Fri, 10 Dec 2021 15:49:06 +0800
Subject: [PATCH 26/28] armcpuhp: set cpu->disabled to true when initialize cpu
The new qemu edition remove the instance init fn of arm cpu. revert is
back and set cpu->disabled to true in it.
Signed-off-by: Huang Shijie <shijie8@gmail.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
target/arm/cpu64.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 2134be0b67..249dc24849 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -895,7 +895,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
{
CPUClass *cc = CPU_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
- CPUState *cs = CPU(oc);
dc->user_creatable = true;
cc->gdb_read_register = aarch64_cpu_gdb_read_register;
@@ -909,11 +908,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc, "aarch64",
"Set on/off to enable/disable aarch64 "
"execution state ");
- /*
- * we start every ARM64 vcpu as disabled possible vcpu. It needs to be
- * enabled explicitly
- */
- cs->disabled = true;
}
static void aarch64_cpu_instance_init(Object *obj)
@@ -924,6 +918,15 @@ static void aarch64_cpu_instance_init(Object *obj)
arm_cpu_post_init(obj);
}
+static void aarch64_cpu_initfn(Object *obj)
+{
+ CPUState *cs = CPU(obj);
+ /*
+ * we start every ARM64 vcpu as disabled possible vcpu. It needs to be
+ * enabled explicitly
+ */
+ cs->disabled = true;
+}
static void cpu_register_class_init(ObjectClass *oc, void *data)
{
ARMCPUClass *acc = ARM_CPU_CLASS(oc);
@@ -952,6 +955,7 @@ static const TypeInfo aarch64_cpu_type_info = {
.parent = TYPE_ARM_CPU,
.instance_size = sizeof(ARMCPU),
.instance_finalize = aarch64_cpu_finalizefn,
+ .instance_init = aarch64_cpu_initfn,
.abstract = true,
.class_size = sizeof(AArch64CPUClass),
.class_init = aarch64_cpu_class_init,
--
2.30.2

View File

@ -0,0 +1,63 @@
From e9376db8e4113e2222543a9a4cdc68a40b78b69c Mon Sep 17 00:00:00 2001
From: Huang Shijie <shijie8@gmail.com>
Date: Fri, 10 Dec 2021 15:51:51 +0800
Subject: [PATCH 27/28] armcpuhp: clean acpi ged call back
The new qemu edition has add acpi ged call back and should clean the
related patch before.
Signed-off-by: Huang Shijie <shijie8@gmail.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/acpi/generic_event_device.c | 28 ----------------------------
1 file changed, 28 deletions(-)
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 22a3fb348b..928593ab2c 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -249,32 +249,6 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
}
}
-static void acpi_ged_device_unplug_request_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
-{
- AcpiGedState *s = ACPI_GED(hotplug_dev);
-
- if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
- acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
- } else {
- error_setg(errp, "virt: device unplug request for the unsupported device"
- " type: %s", object_get_typename(OBJECT(dev)));
- }
-}
-
-static void acpi_ged_device_unplug_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
-{
- AcpiGedState *s = ACPI_GED(hotplug_dev);
-
- if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
- acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp);
- } else {
- error_setg(errp, "virt: device plug request for unsupported device"
- " type: %s", object_get_typename(OBJECT(dev)));
- }
-}
-
static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
{
AcpiGedState *s = ACPI_GED(adev);
@@ -461,8 +435,6 @@ static void acpi_ged_class_init(ObjectClass *class, void *data)
dc->vmsd = &vmstate_acpi_ged;
hc->plug = acpi_ged_device_plug_cb;
- hc->unplug_request = acpi_ged_device_unplug_request_cb;
- hc->unplug = acpi_ged_device_unplug_cb;
hc->unplug_request = acpi_ged_unplug_request_cb;
hc->unplug = acpi_ged_unplug_cb;
--
2.30.2

View File

@ -0,0 +1,230 @@
From e9ec5f6617c1811f244618e3a23c29ea2fb27c6a Mon Sep 17 00:00:00 2001
From: Huang Shijie <shijie8@gmail.com>
Date: Thu, 16 Dec 2021 11:36:25 +0800
Subject: [PATCH 28/28] armcpuhp: fix bug when maxvcpu number larger than 8
The gic version finalized after the use the gic version in
virt_cpu_mp_affinity. But VIRT_GICVERSION_HOST must be determined before
used it. So, must call finalize_gic_version before used gic_version.
For now cpu socket and thread is not supported, thus we need set thread
and socket to 1 if they are larger than 1.
Signed-off-by: Huang Shijie <shijie8@gmail.com>
Signed-off-by: Huang Shijie <shijie8@gmail.com>
---
hw/arm/virt.c | 183 ++++++++++++++++++++++++++++----------------------
1 file changed, 102 insertions(+), 81 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 5c04abb352..23dd2337f6 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1661,85 +1661,6 @@ void virt_machine_done(Notifier *notifier, void *data)
virt_remove_disabled_cpus(vms);
}
-static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
-{
- uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER;
- VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
-
- if (!vmc->disallow_affinity_adjustment) {
- /* Adjust MPIDR like 64-bit KVM hosts, which incorporate the
- * GIC's target-list limitations. 32-bit KVM hosts currently
- * always create clusters of 4 CPUs, but that is expected to
- * change when they gain support for gicv3. When KVM is enabled
- * it will override the changes we make here, therefore our
- * purposes are to make TCG consistent (with 64-bit KVM hosts)
- * and to improve SGI efficiency.
- */
- if (vms->gic_version == VIRT_GIC_VERSION_3) {
- clustersz = GICV3_TARGETLIST_BITS;
- } else {
- clustersz = GIC_TARGETLIST_BITS;
- }
- }
- return arm_cpu_mp_affinity(idx, clustersz);
-}
-
-static void virt_set_memmap(VirtMachineState *vms)
-{
- MachineState *ms = MACHINE(vms);
- hwaddr base, device_memory_base, device_memory_size;
- int i;
-
- vms->memmap = extended_memmap;
-
- for (i = 0; i < ARRAY_SIZE(base_memmap); i++) {
- vms->memmap[i] = base_memmap[i];
- }
-
- if (ms->ram_slots > ACPI_MAX_RAM_SLOTS) {
- error_report("unsupported number of memory slots: %"PRIu64,
- ms->ram_slots);
- exit(EXIT_FAILURE);
- }
-
- /*
- * We compute the base of the high IO region depending on the
- * amount of initial and device memory. The device memory start/size
- * is aligned on 1GiB. We never put the high IO region below 256GiB
- * so that if maxram_size is < 255GiB we keep the legacy memory map.
- * The device region size assumes 1GiB page max alignment per slot.
- */
- device_memory_base =
- ROUND_UP(vms->memmap[VIRT_MEM].base + ms->ram_size, GiB);
- device_memory_size = ms->maxram_size - ms->ram_size + ms->ram_slots * GiB;
-
- /* Base address of the high IO region */
- base = device_memory_base + ROUND_UP(device_memory_size, GiB);
- if (base < device_memory_base) {
- error_report("maxmem/slots too huge");
- exit(EXIT_FAILURE);
- }
- if (base < vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES) {
- base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES;
- }
-
- for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) {
- hwaddr size = extended_memmap[i].size;
-
- base = ROUND_UP(base, size);
- vms->memmap[i].base = base;
- vms->memmap[i].size = size;
- base += size;
- }
- vms->highest_gpa = base - 1;
- if (device_memory_size > 0) {
- ms->device_memory = g_malloc0(sizeof(*ms->device_memory));
- ms->device_memory->base = device_memory_base;
- memory_region_init(&ms->device_memory->mr, OBJECT(vms),
- "device-memory", device_memory_size);
- }
-}
-
/*
* finalize_gic_version - Determines the final gic_version
* according to the gic-version property
@@ -1839,6 +1760,88 @@ static void finalize_gic_version(VirtMachineState *vms)
}
}
+static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
+{
+ uint8_t clustersz = ARM_DEFAULT_CPUS_PER_CLUSTER;
+ VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
+
+ if(vms->gic_version == VIRT_GIC_VERSION_HOST)
+ finalize_gic_version(vms);
+
+ if (!vmc->disallow_affinity_adjustment) {
+ /* Adjust MPIDR like 64-bit KVM hosts, which incorporate the
+ * GIC's target-list limitations. 32-bit KVM hosts currently
+ * always create clusters of 4 CPUs, but that is expected to
+ * change when they gain support for gicv3. When KVM is enabled
+ * it will override the changes we make here, therefore our
+ * purposes are to make TCG consistent (with 64-bit KVM hosts)
+ * and to improve SGI efficiency.
+ */
+ if (vms->gic_version == VIRT_GIC_VERSION_3) {
+ clustersz = GICV3_TARGETLIST_BITS;
+ } else {
+ clustersz = GIC_TARGETLIST_BITS;
+ }
+ }
+ return arm_cpu_mp_affinity(idx, clustersz);
+}
+
+static void virt_set_memmap(VirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ hwaddr base, device_memory_base, device_memory_size;
+ int i;
+
+ vms->memmap = extended_memmap;
+
+ for (i = 0; i < ARRAY_SIZE(base_memmap); i++) {
+ vms->memmap[i] = base_memmap[i];
+ }
+
+ if (ms->ram_slots > ACPI_MAX_RAM_SLOTS) {
+ error_report("unsupported number of memory slots: %"PRIu64,
+ ms->ram_slots);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * We compute the base of the high IO region depending on the
+ * amount of initial and device memory. The device memory start/size
+ * is aligned on 1GiB. We never put the high IO region below 256GiB
+ * so that if maxram_size is < 255GiB we keep the legacy memory map.
+ * The device region size assumes 1GiB page max alignment per slot.
+ */
+ device_memory_base =
+ ROUND_UP(vms->memmap[VIRT_MEM].base + ms->ram_size, GiB);
+ device_memory_size = ms->maxram_size - ms->ram_size + ms->ram_slots * GiB;
+
+ /* Base address of the high IO region */
+ base = device_memory_base + ROUND_UP(device_memory_size, GiB);
+ if (base < device_memory_base) {
+ error_report("maxmem/slots too huge");
+ exit(EXIT_FAILURE);
+ }
+ if (base < vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES) {
+ base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES;
+ }
+
+ for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) {
+ hwaddr size = extended_memmap[i].size;
+
+ base = ROUND_UP(base, size);
+ vms->memmap[i].base = base;
+ vms->memmap[i].size = size;
+ base += size;
+ }
+ vms->highest_gpa = base - 1;
+ if (device_memory_size > 0) {
+ ms->device_memory = g_malloc0(sizeof(*ms->device_memory));
+ ms->device_memory->base = device_memory_base;
+ memory_region_init(&ms->device_memory->mr, OBJECT(vms),
+ "device-memory", device_memory_size);
+ }
+}
+
static void virt_cpu_set_properties(Object *cpuobj, const CPUArchId *cpu_slot)
{
MachineState *ms = MACHINE(qdev_get_machine());
@@ -2928,9 +2931,27 @@ static void virt_smp_parse(MachineState *ms, SMPConfiguration *config, Error **e
unsigned threads = config->has_threads ? config->threads: 1;
unsigned int max_cpus;
+ if(config->has_cores) {
+ config->cores = cpus;
+ cores = cpus;
+ }
if (sockets > 1 || threads > 1) {
- error_report("does not support more than one socket or thread");
- exit(1);
+ printf("does not support more than one socket or thread, will put sockets and threads to cores");
+ if(config->has_cores) {
+ if(config->has_sockets && config->has_threads) {
+ threads = 1;
+ sockets = 1;
+ config->sockets = 1;
+ config->threads = 1;
+ } else if (config->has_sockets) {
+ sockets = 1;
+ config->sockets = 1;
+ } else {
+ config->threads = 1;
+ threads = 1;
+ }
+ } else
+ exit(1);
}
if (cores != cpus) {
--
2.30.2