mirror of
				https://github.com/kata-containers/kata-containers.git
				synced 2025-10-30 08:52:39 +00:00 
			
		
		
		
	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:
		| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
| @@ -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 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user