diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 87660cd..d5f1c52 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1931,6 +1931,64 @@ static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index)
     return topo.pkg_id;
 }
 
+static void pc_add_possible_cpu_descriptor(HotpluggableCPUList **head,
+                                           const char *model_name,
+                                           uint32_t apic_id,
+                                           char *cpu_link)
+{
+    X86CPUTopoInfo topo;
+    HotpluggableCPUList *list_item = g_new0(HotpluggableCPUList, 1);
+    HotpluggableCPU *cpu_item = g_new0(HotpluggableCPU, 1);
+
+    cpu_item->type = x86_cpu_type_name(model_name);
+    cpu_item->arch_id = apic_id;
+    x86_topo_ids_from_apicid(apic_id, smp_cores, smp_threads, &topo);
+    cpu_item->has_socket = true;
+    cpu_item->socket = topo.pkg_id;
+    cpu_item->has_core = true;
+    cpu_item->core = topo.core_id;
+    cpu_item->has_thread = true;
+    cpu_item->thread = topo.smt_id;
+    cpu_item->has_cpu_link = cpu_link;
+    cpu_item->cpu_link = cpu_link;
+
+    list_item->value = cpu_item;
+    list_item->next = *head;
+    *head = list_item;
+}
+
+static HotpluggableCPUList *pc_possible_cpus(MachineState *machine)
+{
+    int i;
+    CPUState *cpu;
+    uint32_t apic_id;
+    gchar **model_pieces;
+    HotpluggableCPUList *head = NULL;
+    DECLARE_BITMAP(found_cpus, ACPI_CPU_HOTPLUG_ID_LIMIT);
+
+    model_pieces = g_strsplit(machine->cpu_model, ",", 2);
+
+    memset(found_cpus, 0, sizeof found_cpus);
+    CPU_FOREACH(cpu) {
+        CPUClass *cc = CPU_GET_CLASS(cpu);
+
+        apic_id = cc->get_arch_id(cpu);
+        set_bit(apic_id, found_cpus);
+        pc_add_possible_cpu_descriptor(&head, model_pieces[0], apic_id,
+                                       object_get_canonical_path(OBJECT(cpu)));
+    }
+
+
+    for (i = 0; i < max_cpus; i++) {
+        apic_id = x86_cpu_apic_id_from_index(i);
+        if (!test_bit(apic_id, found_cpus)) {
+            pc_add_possible_cpu_descriptor(&head, model_pieces[0], apic_id,
+                                           NULL);
+        }
+    }
+    return head;
+}
+
 static void pc_machine_class_init(ObjectClass *oc, void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
@@ -1953,6 +2011,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
     pcmc->save_tsc_khz = true;
     mc->get_hotplug_handler = pc_get_hotpug_handler;
     mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
+    mc->possible_cpus = pc_possible_cpus;
     mc->default_boot_order = "cad";
     mc->hot_add_cpu = pc_hot_add_cpu;
     mc->max_cpus = 255;
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 0f30959..0a29cad 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -57,6 +57,11 @@ bool machine_mem_merge(MachineState *machine);
  *    Set only by old machines because they need to keep
  *    compatibility on code that exposed QEMU_VERSION to guests in
  *    the past (and now use qemu_hw_version()).
+ * @possible_cpus:
+ *    Returns a list of @HotpluggableCPU descriptors
+ *    which includes CPU IDs for present and possible to hotplug CPUs
+ *    and coresponding type an topology information.
+ *    Caller is responsible for freeing returned list.
  */
 struct MachineClass {
     /*< private >*/
@@ -99,6 +104,7 @@ struct MachineClass {
     HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
                                            DeviceState *dev);
     unsigned (*cpu_index_to_socket_id)(unsigned cpu_index);
+    HotpluggableCPUList *(*possible_cpus)(MachineState *machine);
 };
 
 /**
diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h
index 148cc1b..9e9c00c 100644
--- a/include/hw/i386/topology.h
+++ b/include/hw/i386/topology.h
@@ -119,6 +119,21 @@ static inline void x86_topo_ids_from_idx(unsigned nr_cores,
     topo->pkg_id = core_index / nr_cores;
 }
 
+/* Calculate thread/core/package IDs for a specific topology,
+ * based on APIC ID
+ */
+static inline void x86_topo_ids_from_apicid(unsigned apicid,
+                                            unsigned nr_cores,
+                                            unsigned nr_threads,
+                                            X86CPUTopoInfo *topo)
+{
+    topo->smt_id = apicid &
+                   ~(0xFFFFFFFFUL << apicid_smt_width(nr_cores, nr_threads));
+    topo->core_id = (apicid >> apicid_core_offset(nr_cores, nr_threads)) &
+                   ~(0xFFFFFFFFUL << apicid_core_width(nr_cores, nr_threads));
+    topo->pkg_id = apicid >> apicid_pkg_offset(nr_cores, nr_threads);
+}
+
 /* Make APIC ID for the CPU 'cpu_index'
  *
  * 'cpu_index' is a sequential, contiguous ID for the CPU.
diff --git a/monitor.c b/monitor.c
index 73eac17..0c9e6ce 100644
--- a/monitor.c
+++ b/monitor.c
@@ -4241,3 +4241,16 @@ void qmp_dump_skeys(const char *filename, Error **errp)
     error_setg(errp, QERR_FEATURE_DISABLED, "dump-skeys");
 }
 #endif
+
+HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp)
+{
+    MachineState *ms = MACHINE(qdev_get_machine());
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+
+    if (!mc->possible_cpus) {
+        error_setg(errp, QERR_FEATURE_DISABLED, "query-hotpluggable-cpus");
+        return NULL;
+    }
+
+    return mc->possible_cpus(ms);
+}
diff --git a/qapi-schema.json b/qapi-schema.json
index 8d04897..127ba51 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -4083,3 +4083,33 @@
 ##
 { 'enum': 'ReplayMode',
   'data': [ 'none', 'record', 'play' ] }
+
+##
+# @HotpluggableCPU
+#
+# @type: CPU object tyep for usage with device_add command
+# @arch_id: unique number designating the CPU within board
+# @node: NUMA node ID the CPU belongs to, optional
+# @socket: socket number within node/board the CPU belongs to, optional
+# @core: core number within socket the CPU belongs to, optional
+# @thread: thread number within core the CPU belongs to, optional
+# @cpu_link: link to existing CPU object is CPU is present or
+#            omitted if CPU is not present.
+#
+# Since: 2.6
+{ 'struct': 'HotpluggableCPU',
+  'data': { 'type': 'str',
+            'arch_id': 'int',
+            '*node': 'int',
+            '*socket': 'int',
+            '*core': 'int',
+            '*thread': 'int',
+            '*cpu_link': 'str'
+          }
+}
+
+##
+# @query-hotpluggable-cpus
+#
+# Since: 2.6
+{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'] }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 020e5ee..cbe0ba4 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -4818,3 +4818,29 @@ Example:
                  {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840,
                   "pop-vlan": 1, "id": 251658240}
    ]}
+
+EQMP
+
+    {
+        .name       = "query-hotpluggable-cpus",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_query_hotpluggable_cpus,
+    },
+
+SQMP
+Show  existing/possible CPUs
+-------------------------------
+
+Arguments: None.
+
+Example for x86 target started with -smp 2,sockets=2,cores=1,threads=3,maxcpus=6:
+
+-> { "execute": "query-hotpluggable-cpus" }
+<- {"return": [
+     {"core": 0, "socket": 1, "thread": 2, "arch_id": 6, "type": "qemu64-x86_64-cpu"},
+     {"core": 0, "socket": 1, "thread": 1, "arch_id": 5, "type": "qemu64-x86_64-cpu"},
+     {"core": 0, "socket": 1, "thread": 0, "arch_id": 4, "type": "qemu64-x86_64-cpu"},
+     {"core": 0, "socket": 0, "thread": 2, "arch_id": 2, "type": "qemu64-x86_64-cpu"},
+     {"core": 0, "arch_id": 1, "socket": 0, "thread": 1, "type": "qemu64-x86_64-cpu", "cpu_link": "/machine/unattached/device[3]"},
+     {"core": 0, "arch_id": 0, "socket": 0, "thread": 0, "type": "qemu64-x86_64-cpu", "cpu_link": "/machine/unattached/device[0]"}
+   ]}'
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 3918f01..c9b33b8 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -647,7 +647,7 @@ static void add_flagname_to_bitmaps(const char *flagname,
 /* Return type name for a given CPU model name
  * Caller is responsible for freeing the returned string.
  */
-static char *x86_cpu_type_name(const char *model_name)
+char *x86_cpu_type_name(const char *model_name)
 {
     return g_strdup_printf(X86_CPU_TYPE_NAME("%s"), model_name);
 }
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index a990ea7..84e8183 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -1354,4 +1354,5 @@ void enable_compat_apic_id_mode(void);
 void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f,
                                    fprintf_function cpu_fprintf, int flags);
 
+char *x86_cpu_type_name(const char *model_name);
 #endif /* CPU_I386_H */
