diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index d7d0b62..2f3c8e2 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -291,6 +291,7 @@ static void s390_cpu_initfn(Object *obj)
     cs->exception_index = EXCP_HLT;
     object_property_add(OBJECT(cpu), "id", "int64_t", s390x_cpu_get_id,
                         s390x_cpu_set_id, NULL, NULL, NULL);
+    s390_cpu_model_register_props(obj);
 #if !defined(CONFIG_USER_ONLY)
     qemu_get_timedate(&tm, 0);
     env->tod_offset = TOD_UNIX_EPOCH +
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 3b76654..d03f0f1 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -632,6 +632,7 @@ extern void subsystem_reset(void);
 
 void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 #define cpu_list s390_cpu_list
+void s390_cpu_model_register_props(Object *obj);
 void s390_cpu_model_class_register_props(ObjectClass *oc);
 void s390_realize_cpu_model(CPUState *cs, Error **errp);
 ObjectClass *s390_cpu_class_by_name(const char *name);
diff --git a/target-s390x/cpu_models.c b/target-s390x/cpu_models.c
index 5045aef..1daa208 100644
--- a/target-s390x/cpu_models.c
+++ b/target-s390x/cpu_models.c
@@ -14,6 +14,7 @@
 #include "cpu.h"
 #include "gen-features.h"
 #include "qapi/error.h"
+#include "qapi/visitor.h"
 #ifndef CONFIG_USER_ONLY
 #include "sysemu/arch_init.h"
 #endif
@@ -103,8 +104,24 @@ void s390_cpu_list(FILE *f, fprintf_function print)
         f = f,
         print = print,
     };
+    S390FeatGroup group;
+    S390Feat feat;
 
     object_class_foreach(print_cpu_model_list, TYPE_S390_CPU, false, &info);
+
+    (*print)(f, "\nRecognized feature flags:\n");
+    for (feat = 0; feat < S390_FEAT_MAX; feat++) {
+        const S390FeatDef *def = s390_feat_def(feat);
+
+        (*print)(f, "%-20s %-50s\n", def->name, def->desc);
+    }
+
+    (*print)(f, "\nRecognized feature groups:\n");
+    for (group = 0; group < S390_FEAT_GROUP_MAX; group++) {
+        const S390FeatGroupDef *def = s390_feat_group_def(group);
+
+        (*print)(f, "%-20s %-50s\n", def->name, def->desc);
+    }
 }
 
 #ifndef CONFIG_USER_ONLY
@@ -151,6 +168,138 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp)
     }
 }
 
+static void get_feature(Object *obj, Visitor *v, const char *name,
+                        void *opaque, Error **errp)
+{
+    S390Feat feat = (S390Feat) opaque;
+    S390CPU *cpu = S390_CPU(obj);
+    bool value;
+
+    if (!cpu->model) {
+        error_setg(errp, "Details about the host CPU model are not available, "
+                         "features cannot be queried.");
+        return;
+    }
+
+    value = test_bit(feat, cpu->model->features);
+    visit_type_bool(v, name, &value, errp);
+}
+
+static void set_feature(Object *obj, Visitor *v, const char *name,
+                        void *opaque, Error **errp)
+{
+    S390Feat feat = (S390Feat) opaque;
+    DeviceState *dev = DEVICE(obj);
+    S390CPU *cpu = S390_CPU(obj);
+    bool value;
+
+    if (dev->realized) {
+        error_setg(errp, "Attempt to set property '%s' on '%s' after "
+                   "it was realized", name, object_get_typename(obj));
+        return;
+    } else if (!cpu->model) {
+        error_setg(errp, "Details about the host CPU model are not available, "
+                         "features cannot be changed.");
+        return;
+    }
+
+    visit_type_bool(v, name, &value, errp);
+    if (*errp) {
+        return;
+    }
+    if (value) {
+        if (!test_bit(feat, cpu->model->def->full_feat)) {
+            error_setg(errp, "Feature '%s' is not available for CPU model '%s',"
+                       " it was introduced with later models.",
+                       name, cpu->model->def->name);
+            return;
+        }
+        set_bit(feat, cpu->model->features);
+    } else {
+        clear_bit(feat, cpu->model->features);
+    }
+}
+
+static void get_feature_group(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
+{
+    S390FeatGroup group = (S390FeatGroup) opaque;
+    const S390FeatGroupDef *def = s390_feat_group_def(group);
+    S390CPU *cpu = S390_CPU(obj);
+    S390FeatBitmap tmp;
+    bool value;
+
+    if (!cpu->model) {
+        error_setg(errp, "Details about the host CPU model are not available, "
+                         "features cannot be queried.");
+        return;
+    }
+
+    /* a group is enabled if all features are enabled */
+    bitmap_and(tmp, cpu->model->features, def->feat, S390_FEAT_MAX);
+    value = bitmap_equal(tmp, def->feat, S390_FEAT_MAX);
+    visit_type_bool(v, name, &value, errp);
+}
+
+static void set_feature_group(Object *obj, Visitor *v, const char *name,
+                              void *opaque, Error **errp)
+{
+    S390FeatGroup group = (S390FeatGroup) opaque;
+    const S390FeatGroupDef *def = s390_feat_group_def(group);
+    DeviceState *dev = DEVICE(obj);
+    S390CPU *cpu = S390_CPU(obj);
+    bool value;
+
+    if (dev->realized) {
+        error_setg(errp, "Attempt to set property '%s' on '%s' after "
+                   "it was realized", name, object_get_typename(obj));
+        return;
+    } else if (!cpu->model) {
+        error_setg(errp, "Details about the host CPU model are not available, "
+                         "features cannot be changed.");
+        return;
+    }
+
+    visit_type_bool(v, name, &value, errp);
+    if (*errp) {
+        return;
+    }
+    if (value) {
+        /* groups are added in one shot, so an intersect is sufficient */
+        if (!bitmap_intersects(def->feat, cpu->model->def->full_feat,
+                               S390_FEAT_MAX)) {
+            error_setg(errp, "Group '%s' is not available for CPU model '%s',"
+                       " it was introduced with later models.",
+                       name, cpu->model->def->name);
+            return;
+        }
+        bitmap_or(cpu->model->features, cpu->model->features, def->feat,
+                  S390_FEAT_MAX);
+    } else {
+        bitmap_andnot(cpu->model->features, cpu->model->features, def->feat,
+                      S390_FEAT_MAX);
+    }
+}
+
+void s390_cpu_model_register_props(Object *obj)
+{
+    S390FeatGroup group;
+    S390Feat feat;
+
+    for (feat = 0; feat < S390_FEAT_MAX; feat++) {
+        const S390FeatDef *def = s390_feat_def(feat);
+        object_property_add(obj, def->name, "bool", get_feature,
+                            set_feature, NULL, (void *) feat, NULL);
+        object_property_set_description(obj, def->name, def->desc , NULL);
+    }
+    for (group = 0; group < S390_FEAT_GROUP_MAX; group++) {
+        const S390FeatGroupDef *def = s390_feat_group_def(group);
+        object_property_add(obj, def->name, "bool", get_feature_group,
+                            set_feature_group, NULL, (void *) group, NULL);
+        object_property_set_description(obj, def->name, def->desc , NULL);
+    }
+}
+
 static void s390_cpu_model_initfn(Object *obj)
 {
     S390CPU *cpu = S390_CPU(obj);
