For KVM vcpu models, we add properties for all writable
ID reg fields. This relies on the idreg writable mask retrieved
from KVM, which is matched aginst the register description
found in target/arm/cpu-idregs.h.inc.

The code is prepared to extend this capability to all ID
register fields independently on their writability. This will
be needed for named vcpu models which may want to relax some
values to the host supported one.

REVIDR_EL1 and AIDR_EL1 are writable but they do not expose any
field. So they will need to be handled separately.

The setter and getter are not yet implemented and currently
return an error.

Note in the future, other acceleration types may benefit from
those properties.

Signed-off-by: Eric Auger <[email protected]>

---
v9 -> v10
- instead of parsing the writable mask, scroll through the fields
  but still do some sanity checks. Add the writable param
- move the code to cpu64.c Do not pass any ARM64SysReg handle
  anymore. Rename the functions

v7 -> v8
- scratch the '!' in the error_setg
- addapt to the removal of struct ArmIdRegArchVal

v5 -> v6
- for enum values, check against value value when setting the prop
- do not check writable_map flag anymore

v4 -> v5:
- free prop_name
- check cpu->writable_map as a preamble in kvm_arm_expose_idreg_properties
---
 target/arm/cpu64.c      | 113 ++++++++++++++++++++++++++++++++++++++++
 target/arm/trace-events |   6 +++
 2 files changed, 119 insertions(+)

diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 64103df0a7..b1738c9be3 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -37,6 +37,8 @@
 #include "hw/core/qdev-properties.h"
 #include "internals.h"
 #include "cpu-features.h"
+#include "cpu-idregs.h"
+#include "trace.h"
 
 /* convert between <register>_IDX and SYS_<register> */
 #define DEF(NAME, OP0, OP1, CRN, CRM, OP2)      \
@@ -798,6 +800,116 @@ static void kvm_arm_set_cpreg_mig_tolerances(ARMCPU *cpu)
     arm_register_cpreg_mig_tolerance(cpu, KVM_REG_ARM_FW_FEAT_BMAP_REG(3),
                                      UINT64_MAX, 0, ToleranceOnlySrcTestValue);
 }
+
+static void set_sysreg_prop(Object *obj, Visitor *v,
+                            const char *name, void *opaque,
+                            Error **errp)
+{
+    error_setg(errp, "%s setter not yet implemented", name);
+}
+
+static void get_sysreg_prop(Object *obj, Visitor *v,
+                            const char *name, void *opaque,
+                            Error **errp)
+{
+    error_setg(errp, "%s getter not yet implemented", name);
+}
+
+/**
+ * aarch64_add_idreg_properties: Add SYSREG_<REG>_<FIELD> properties
+ * for named fields of an ID register
+ *
+ * @obj: CPU object
+ * @reg: Description of the sysreg
+ * @writable_only: if set, add a property only for writable fields,
+ * otherwise, add properties unconditionnally
+ *
+ * Independently on @writable_only, always check the consistency between
+ * writable_mask and register fields description: if a field is writable
+ * make sure all its bits are writable. Also make sure only named fields
+ * are writable.
+ */
+static int
+aarch64_add_one_idreg_properties(Object *obj, ARM64SysReg *reg,
+                                 bool writable_only)
+{
+    uint64_t map = reg->writable_mask;
+    int nb_sysreg_props = 0;
+
+    for (int i = 0; i < reg->fields_count; i++) {
+        ARM64SysRegField *field = &reg->fields[i];
+        int lower = field->shift;
+        int length = field->length;
+        int upper = lower + length - 1;
+        uint64_t field_writable_mask = extract64(map, lower, length);
+        uint64_t field_mask = MAKE_64BIT_MASK(lower, length);
+        char *prop_name;
+
+        /* skip reserved fields, but still account them in writable_mask */
+        if (g_str_equal(field->name, "RES0") ||
+            g_str_equal(field->name, "RES1") ||
+            g_str_equal(field->name, "RAZ")) {
+            map &= ~field_mask;
+            continue;
+        }
+
+        if (field_writable_mask &&
+            field_writable_mask != MAKE_64BIT_MASK(0, length)) {
+            /* only a part of the field is writable, this should never happen 
*/
+            error_report("field %s is not fully writable! (0x%"PRIx64")",
+                         field->name, field_writable_mask);
+        }
+        map = map & ~field_mask;
+
+        if (writable_only && !field_writable_mask) {
+            continue;
+        }
+
+        prop_name = g_strdup_printf("SYSREG_%s_%s", reg->name, field->name);
+        object_property_add(obj, prop_name, "uint64",
+                            get_sysreg_prop, set_sysreg_prop, NULL, field);
+        trace_add_idreg_property(prop_name, lower, upper, field_writable_mask);
+        g_free(prop_name);
+        nb_sysreg_props++;
+    }
+    if (map) {
+        error_report("%s has writable bits which do not match "
+                     "any named field! (0x%" PRIx64")", reg->name, map);
+    }
+    trace_nb_sysreg_props(reg->name, nb_sysreg_props);
+    return 0;
+}
+
+
+/**
+ * aarch64_add_idreg_properties:
+ *
+ * Add SYSREG_<REG>_<FIELD> properties for every IDReg fields
+ *
+ * @cpu: cpu the properties are attached to
+ * @writable_only:  if set, properties are added only if the field is
+ * writable. Otherwise, added unconditionnally
+ */
+static void aarch64_add_idreg_properties(Object *obj, bool writable_only)
+{
+    for (int i = 0; i < NUM_ID_IDX; i++) {
+        ARM64SysReg *sysregdesc = &arm64_id_regs[i];
+
+        if (writable_only && !sysregdesc->writable_mask) {
+            continue;
+        }
+        /*
+         * special case REVIDR_EL1 and AIDR_EL1 which are writable but
+         * do not expose named fields. They will need to be handled
+         * separately
+         */
+        if (!strcmp(sysregdesc->name, "REVIDR_EL1") ||
+            !strcmp(sysregdesc->name, "AIDR_EL1")) {
+            continue;
+        }
+        aarch64_add_one_idreg_properties(obj, sysregdesc, true);
+    }
+}
 #endif
 
 void aarch64_host_initfn(Object *obj)
@@ -815,6 +927,7 @@ void aarch64_host_initfn(Object *obj)
     kvm_arm_set_cpreg_mig_tolerances(cpu);
     kvm_arm_set_cpu_features_from_host(cpu);
     aarch64_add_sve_properties(obj);
+    aarch64_add_idreg_properties(obj, true);
 #elif defined(CONFIG_HVF)
     hvf_arm_set_cpu_features_from_host(cpu);
 #elif defined(CONFIG_WHPX)
diff --git a/target/arm/trace-events b/target/arm/trace-events
index 8c7faf57c7..ddc79eb666 100644
--- a/target/arm/trace-events
+++ b/target/arm/trace-events
@@ -19,6 +19,12 @@ get_host_cpu_idregs(const char *name, uint64_t value) 
"scratch vcpu host value f
 arm_cpu_reset(uint64_t mp_aff) "cpu %" PRIu64
 arm_emulate_firmware_reset(uint64_t mp_aff, unsigned target_el) "cpu %" PRIu64 
" @EL%u"
 
+# cpu64.c
+add_idreg_property(const char* name, int lower, int upper, bool writable) "%s 
[%d:%d] writable=%d"
+get_sysreg_prop(const char *name, uint64_t value) "%s 0x%"PRIx64
+set_sysreg_prop(const char *name, uint64_t old, uint64_t mask, uint64_t 
field_value, uint64_t new) "%s old reg value=0x%"PRIx64" mask=0x%"PRIx64" new 
field value=0x%"PRIx64" new reg value=0x%"PRIx64
+nb_sysreg_props(const char *name, int count) "%s: %d SYSREG properties"
+
 # arm-powerctl.c
 arm_powerctl_set_cpu_on(uint64_t mp_aff, unsigned target_el, const char *mode, 
uint64_t entry, uint64_t context_id) "cpu %" PRIu64 " (EL %u, %s) @ 0x%" PRIx64 
" with R0 = 0x%" PRIx64
 arm_powerctl_set_cpu_on_and_reset(uint64_t mp_aff) "cpu %" PRIu64
-- 
2.53.0


Reply via email to