diff --git a/target-s390x/cpu-models.c b/target-s390x/cpu-models.c
index 25147a4..19bbb30 100644
--- a/target-s390x/cpu-models.c
+++ b/target-s390x/cpu-models.c
@@ -12,6 +12,7 @@
 
 #include "cpu.h"
 #include "cpu-models.h"
+#include "qemu/error-report.h"
 
 #define S390_FAC_NAME(n, _cpu_id)               \
     glue(glue(glue(FAC, n), _), _cpu_id)
@@ -86,8 +87,42 @@ S390_PROC_DEF("2827-ga1", CPU_S390_2827_GA1, "IBM zEnterprise EC12 GA1")
 S390_PROC_DEF("2827-ga2", CPU_S390_2827_GA2, "IBM zEnterprise EC12 GA2")
 S390_PROC_DEF("2828-ga1", CPU_S390_2828_GA1, "IBM zEnterprise BC12 GA1")
 
+/* some types for calls to g_list_foreach() with arguments */
+typedef struct ArgTCV {
+    unsigned short type;
+    unsigned short class;
+    bool valid;
+} ArgTCV;
+
+typedef struct ArgTGV {
+    unsigned short type;
+    unsigned short gen;
+    bool valid;
+} ArgTGV;
+
+typedef struct ArgTC {
+    unsigned short type;
+    unsigned short class;
+} ArgTC;
+
+typedef struct ArgPH {
+    S390MachineProps *prop;
+    S390CPUClass *host_cc;
+} ArgPH;
+
+typedef struct ArgH {
+    S390CPUClass *host_cc;
+} ArgH;
+
+typedef struct ArgTG {
+    unsigned short type;
+    unsigned short ga;
+} ArgTG;
+
 /* S390 CPU aliases can be added dynamically to this list */
 GSList *s390_cpu_aliases;
+bool s390_cpu_classes_prepared;
+bool s390_use_sofl;
 
 static inline unsigned long bit_in_word(unsigned int nr)
 {
@@ -107,6 +142,21 @@ static inline int test_facility(unsigned long nr, unsigned long *fac_list)
     return (*ptr & bit_in_word(nr)) != 0;
 }
 
+/* compare order of two cpu classes for ascending sort */
+gint s390_cpu_class_asc_order_compare(gconstpointer a, gconstpointer b)
+{
+    S390CPUClass *cc_a = S390_CPU_CLASS((ObjectClass *) a);
+    S390CPUClass *cc_b = S390_CPU_CLASS((ObjectClass *) b);
+
+    if (cc_a->mach->order < cc_b->mach->order) {
+        return -1;
+    }
+    if (cc_a->mach->order > cc_b->mach->order) {
+        return 1;
+    }
+    return 0;
+}
+
 static gint s390_cpu_compare_class_name(gconstpointer a, gconstpointer b)
 {
     ObjectClass *oc = (ObjectClass *)a;
@@ -167,3 +217,354 @@ int set_s390_cpu_alias(const char *name, const char *model)
 
     return 0;
 }
+
+/* return machine class for specific machine type */
+static void s390_machine_class_test_cpu_class(gpointer data, gpointer user_data)
+{
+    S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+    ArgTCV *arg = user_data;
+
+    if (arg->valid || !cc->proc->type || arg->type != cc->proc->type) {
+        return;
+    }
+
+    arg->class = cc->mach->class;
+    arg->valid = true;
+}
+
+/* return machine class by machine type */
+static unsigned short machine_class(unsigned short type, void *user_data)
+{
+    GSList *list = object_class_get_list(TYPE_S390_CPU, false);
+    ArgTC  *arg = user_data;
+    ArgTCV arg_class;
+
+    if (arg->type != type) {
+        arg->class = 0;
+    }
+    if (!arg->class) {
+        arg_class.type = type;
+        arg_class.class = 0;
+        arg_class.valid = false;
+        g_slist_foreach(list, (GFunc) s390_machine_class_test_cpu_class,
+                        &arg_class);
+        g_slist_free(list);
+        if (arg_class.valid) {
+            arg->class = arg_class.class;
+        }
+    }
+    arg->type = type;
+
+    return arg->class;
+}
+
+/* return CMOS generation for specific machine type */
+static void s390_machine_class_test_cpu_gen(gpointer data, gpointer user_data)
+{
+    S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+    ArgTGV *arg = user_data;
+
+    if (arg->valid) {
+        return;
+    }
+
+    if (arg->type == cc->proc->type) {
+        arg->gen = cc->proc->gen;
+        arg->valid = true;
+    }
+}
+
+/* return CMOS generation by machine type */
+static uint16_t machine_gen(unsigned short type)
+{
+    GSList *list = object_class_get_list(TYPE_S390_CPU, false);
+    ArgTGV arg_gen;
+
+    arg_gen.type = type;
+    arg_gen.gen = 0;
+    arg_gen.valid = false;
+    g_slist_foreach(list, (GFunc) s390_machine_class_test_cpu_gen,
+                    &arg_gen);
+    g_slist_free(list);
+
+    return arg_gen.gen;
+}
+
+/* mark cpu class, used in host cpu model case */
+static void s390_mark_host_cpu_class(gpointer data, gpointer user_data)
+{
+    S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+    ArgPH *arg = user_data;
+    ArgTC arg_tc;
+
+    if (!cc->is_active) {
+        return;
+    }
+
+    arg_tc.type = 0;
+    arg_tc.class = 0;
+    if (cc->mach->class != machine_class(
+            cpuid_type(arg->prop->cpuid), &arg_tc)) {
+        /* sort out machines that differ from host machine class */
+        return;
+    }
+    if (!arg->host_cc) {
+        /* use first matching machine type */
+        cc->is_host = true;
+        arg->host_cc = cc;
+        return;
+    }
+    if (cc->proc->gen > machine_gen(cpuid_type(arg->prop->cpuid))) {
+        /* sort out CMOS generations later than hosts generation */
+        cc->is_active = false;
+        return;
+    }
+    if (cc->mach->order > arg->host_cc->mach->order) {
+        /* select later machine as host */
+        arg->host_cc->is_host = false;
+        cc->is_host = true;
+        arg->host_cc = cc;
+    }
+}
+
+/* update a specific cpu model class with host retrieved configuration */
+static void s390_update_cpu_class(gpointer data, gpointer user_data)
+{
+    ObjectClass *oc = data;
+    S390MachineProps *prop = user_data;
+    S390CPUClass *cc = S390_CPU_CLASS(oc);
+    unsigned int i;
+
+    if (!cc->proc->type) {
+        return;
+    }
+
+    /* set processor identifier */
+    cc->proc->id = cpuid_id(prop->cpuid);
+
+    /*
+     * define model specific IBC value in current host context.
+     * IBC was introduced with CMOS version 10 i.e. type 2097.
+     * For older CPUs it is assumed to be 0x000. The BC system
+     * has always the same IBC version as the previous EC system.
+     * If the host supports IBC but not the requested type, it
+     * will be set to the lowest supported value.
+     */
+    if (has_ibc(prop->ibc_range)) {
+        if (cc->proc->gen >= S390_CMOS_G10) {
+            cc->proc->ibc = ((cc->proc->gen - S390_CMOS_G10) << 4);
+            cc->proc->ibc += cc->mach->ga;
+            if (cc->mach->class == S390_BC) {
+                cc->proc->ibc++;
+            }
+            if (cc->proc->ibc < lowest_ibc(prop->ibc_range)) {
+                cc->proc->ibc = lowest_ibc(prop->ibc_range);
+            }
+            if (cc->proc->ibc > latest_ibc(prop->ibc_range)) {
+                cc->proc->ibc = latest_ibc(prop->ibc_range);
+            }
+        } else {
+            cc->proc->ibc = lowest_ibc(prop->ibc_range);
+        }
+    }
+
+    /* set desired facility list of class */
+    for (i = 0; i < S390_FAC_LIST_SIZE_UINT64; i++) {
+        cc->fac_list[i] = prop->fac_mask[i] & cc->proc->fac_list[i];
+    }
+
+    /* mark cpu class inactive if not all desired facility bits are available */
+    for (i = 0; i < S390_FAC_LIST_SIZE_BIT && cc->is_active; i++) {
+        if (test_facility(i, cc->fac_list) &&
+            !test_facility(i, prop->hard_fac_list)) {
+            cc->is_active = false;
+        }
+    }
+
+    /* extend desired facility list by offered soft facility list */
+    if (s390_use_sofl) {
+        for (i = 0; i < S390_FAC_LIST_SIZE_UINT64; i++) {
+            cc->fac_list[i] |= prop->fac_mask[i] & prop->soft_fac_list[i];
+        }
+    }
+}
+
+/* a cpu class that is newer then the current host */
+static void s390_deactivate_not_supported_cpu_class(gpointer data,
+                                                    gpointer user_data)
+{
+    S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+    ArgH *arg = user_data;
+
+    if (!cc->is_active) {
+        return;
+    }
+    if (cc->mach->order > arg->host_cc->mach->order) {
+        cc->is_active = false;
+    }
+}
+
+/* set alias by type and ga */
+static int set_s390_cpu_alias_by_type_ga(unsigned short type, unsigned short ga)
+{
+    char name[8], model[16];
+
+    snprintf(name, sizeof(name), "%04x", type);
+    snprintf(model, sizeof(model), "%04x-ga%u", type, ga);
+
+    return set_s390_cpu_alias(name, model);
+}
+
+/* set alias if system has latest ga of a type */
+static void s390_set_ga_alias_from_cpu_class(gpointer data, gpointer user_data)
+{
+    S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+    ArgTG *arg = user_data;
+
+    if (!cc->is_active) {
+        return;
+    }
+    if (!arg->type) {
+        arg->type = cc->proc->type;
+    }
+    if (cc->proc->type == arg->type) {
+        arg->ga = cc->mach->ga;
+        return;
+    }
+    set_s390_cpu_alias_by_type_ga(arg->type, arg->ga);
+    arg->type = cc->proc->type;
+    arg->ga = cc->mach->ga;
+}
+
+/* set host marked cpu class as alias to respective class */
+static void s390_set_host_alias_from_cpu_class(gpointer data,
+                                               gpointer user_data)
+{
+    S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+    char model[16];
+
+    if (!cc->is_active || !cc->is_host) {
+        return;
+    }
+    snprintf(model, sizeof(model), "%04x-ga%u", cc->proc->type, cc->mach->ga);
+    set_s390_cpu_alias("host", model);
+}
+
+/*
+ * apply host properties retrieved from KVM to cpu model classes,
+ * then find cpu model host and define further aliases
+ */
+int s390_setup_cpu_classes(S390MachineProps *prop)
+{
+    GSList *list;
+    ArgPH arg_host;
+    ArgH  arg_deactivate;
+    ArgTG arg_alias;
+
+    list = object_class_get_list(TYPE_S390_CPU, false);
+    list = g_slist_sort(list, s390_cpu_class_asc_order_compare);
+
+    /* update cpu classes with KVM properties */
+    g_slist_foreach(list, (GFunc) s390_update_cpu_class, (gpointer) prop);
+
+    /* define cpu model "host" */
+    arg_host.prop = prop;
+    arg_host.host_cc = NULL;
+    g_slist_foreach(list, (GFunc) s390_mark_host_cpu_class,
+                    (gpointer) &arg_host);
+
+    if (!arg_host.host_cc) {
+        error_report("Failed to mark host cpu class: %m");
+        return -EINVAL;
+    }
+
+    /* invalidate cpu classes not supported by this host */
+    arg_deactivate.host_cc = arg_host.host_cc;
+    g_slist_foreach(list, (GFunc) s390_deactivate_not_supported_cpu_class,
+                    &arg_deactivate);
+
+    /* set machine type aliases to latest ga of model (e.g. 2064 -> 2064-ga3) */
+    arg_alias.type = 0;
+    arg_alias.ga = 0;
+    g_slist_foreach(list, (GFunc) s390_set_ga_alias_from_cpu_class, &arg_alias);
+    set_s390_cpu_alias_by_type_ga(arg_alias.type, arg_alias.ga);
+
+    /* set aliases for common model names to machine types */
+    set_s390_cpu_alias("z900",   "2064");
+    set_s390_cpu_alias("z800",   "2066");
+    set_s390_cpu_alias("z990",   "2084");
+    set_s390_cpu_alias("z890",   "2086");
+    set_s390_cpu_alias("z9-109", "2094-ga1");
+    set_s390_cpu_alias("z9",     "2094");
+    set_s390_cpu_alias("z9-ec",  "2094");
+    set_s390_cpu_alias("z9-bc",  "2096");
+    set_s390_cpu_alias("z10",    "2097");
+    set_s390_cpu_alias("z10-ec", "2097");
+    set_s390_cpu_alias("z10-bc", "2098");
+    set_s390_cpu_alias("z196",   "2817");
+    set_s390_cpu_alias("z114",   "2818");
+    set_s390_cpu_alias("zEC12",  "2827");
+    set_s390_cpu_alias("zBC12",  "2828");
+
+    /* set alias for cpu model "host" */
+    g_slist_foreach(list, (GFunc) s390_set_host_alias_from_cpu_class, NULL);
+
+    g_slist_free(list);
+
+    s390_cpu_classes_prepared = true;
+
+    return 0;
+}
+
+/* list all supported cpu models and alias names */
+void s390_cpu_list_entry(gpointer data, gpointer user_data)
+{
+    ObjectClass *alias_oc, *oc = data;
+    CPUListState *s = user_data;
+    DeviceClass  *dc = DEVICE_CLASS(oc);
+    S390CPUClass *cc = S390_CPU_CLASS(oc);
+    const char *typename = object_class_get_name(oc);
+    S390CPUAlias *alias;
+    GSList *item;
+    char *name;
+
+    if (!cc->is_active) {
+        return;
+    }
+    name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_S390_CPU));
+    (*s->cpu_fprintf)(s->file, "s390 %-10s %s\n", name, dc->desc);
+
+    for (item = s390_cpu_aliases; item != NULL; item = item->next) {
+        alias = (S390CPUAlias *) item->data;
+        alias_oc = s390_cpu_class_by_name(alias->model);
+        if (alias_oc != oc) {
+            continue;
+        }
+        (*s->cpu_fprintf)(s->file, "s390 %-10s (alias for %s)\n",
+                          alias->name, name);
+    }
+
+    g_free(name);
+}
+
+/* copy cpu class properties to another cpu class */
+void s390_update_cpu_class_properties(S390CPUClass *dst, S390CPUClass *src)
+{
+    if (!dst || !src->is_active) {
+        return;
+    }
+    dst->is_active = src->is_active;
+    dst->is_host = src->is_host;
+    memcpy(dst->fac_list, src->fac_list,
+           S390_FAC_LIST_SIZE_BYTE);
+    dst->mach->order = src->mach->order;
+    dst->mach->class = src->mach->class;
+    dst->mach->ga = src->mach->ga;
+    dst->proc->ver = src->proc->ver;
+    dst->proc->id = src->proc->id;
+    dst->proc->type = src->proc->type;
+    dst->proc->ibc = src->proc->ibc;
+    dst->proc->gen = src->proc->gen;
+    memcpy(dst->proc->fac_list, src->proc->fac_list,
+           S390_FAC_LIST_SIZE_BYTE);
+}
diff --git a/target-s390x/cpu-models.h b/target-s390x/cpu-models.h
index 3533c96..cc917d4 100644
--- a/target-s390x/cpu-models.h
+++ b/target-s390x/cpu-models.h
@@ -37,12 +37,27 @@
 #define FAC_BIT(WORD, BIT) \
     (BIT / 64 == WORD ? 1ull << (63 - BIT % 64) : 0)
 
+/* first s390 CMOS generation supporting IBC */
+#define S390_CMOS_G10 0xa
+
 #define cpu_type(x)       (((x) >>  0) & 0xffff)
 #define cpu_order(x)      (((x) >> 16) & 0xffff)
 #define cpu_ga(x)         (((x) >> 16) & 0xf)
 #define cpu_class(x)      (((x) >> 20) & 0x3)
 #define cpu_generation(x) (((x) >> 24) & 0xff)
 
+#define cpuid_type(x)     (((x) >> 16) & 0xffff)
+#define cpuid_id(x)       (((x) >> 32) & 0xffffff)
+#define cpuid_ver(x)      (((x) >> 56) & 0xff)
+
+#define type_cpuid(x)     ((uint64_t)((x) & 0xffff) << 16)
+#define id_cpuid(x)       ((uint64_t)((x) & 0xffffff) << 32)
+#define ver_cpuid(x)      ((uint64_t)((x) & 0xff) << 56)
+
+#define lowest_ibc(x) (((x) >> 16) & 0xfff)
+#define latest_ibc(x) ((x) & 0xfff)
+#define has_ibc(x)    (lowest_ibc(x) != 0x0)
+
 ObjectClass *s390_cpu_class_by_name(const char *name);
 int set_s390_cpu_alias(const char *name, const char *model);
 
@@ -71,10 +86,20 @@ typedef struct S390MachineProps {
     uint64_t soft_fac_list[S390_ARCH_FAC_LIST_SIZE_UINT64];
 } S390MachineProps;
 
+/* indicates the cpu classes have been successfully updated */
+extern bool s390_cpu_classes_prepared;
+
+/* indicates use of soft facilities is requested */
+extern bool s390_use_sofl;
+
 int kvm_s390_has_cpu_model_call(uint64_t attr);
 int kvm_s390_get_processor_props(S390ProcessorProps *prob);
 int kvm_s390_set_processor_props(S390ProcessorProps *prob);
 int kvm_s390_get_machine_props(S390MachineProps *prob);
+int s390_setup_cpu_classes(S390MachineProps *prop);
+gint s390_cpu_class_asc_order_compare(gconstpointer a, gconstpointer b);
+void s390_cpu_list_entry(gpointer data, gpointer user_data);
+void s390_update_cpu_class_properties(S390CPUClass *dst, S390CPUClass *src);
 
 /*
  * bits 0-7   : CMOS generation
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index 5e292e7..741f3ce 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -41,7 +41,31 @@
 void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf)
 {
 #ifdef CONFIG_KVM
-    (*cpu_fprintf)(f, "s390 %16s\n", "host");
+    CPUListState s = {
+        .file = f,
+        .cpu_fprintf = cpu_fprintf,
+    };
+    GSList *list;
+
+    if (kvm_enabled()) {
+        list = object_class_get_list(TYPE_S390_CPU, false);
+        list = g_slist_sort(list, s390_cpu_class_asc_order_compare);
+        g_slist_foreach(list, s390_cpu_list_entry, &s);
+        g_slist_free(list);
+    } else {
+#endif
+        (*cpu_fprintf)(f, "s390 host\n");
+#ifdef CONFIG_KVM
+    }
+#endif
+}
+
+bool s390_cpudesc_ready(void)
+{
+#ifdef CONFIG_KVM
+    return s390_cpu_classes_prepared;
+#else
+    return true;
 #endif
 }
 
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index aad277a..7c1c431 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -519,6 +519,9 @@ static inline bool css_present(uint8_t cssid)
 void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 #define cpu_list s390_cpu_list
 
+bool s390_cpudesc_ready(void);
+#define cpudesc_ready s390_cpudesc_ready
+
 #include "exec/exec-all.h"
 
 #define EXCP_EXT 1 /* external interrupt */
