aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/arm/cpu.c19
-rw-r--r--target/arm/cpu.h19
-rw-r--r--target/arm/cpu64.c192
-rw-r--r--target/arm/helper.c10
-rw-r--r--target/arm/monitor.c12
5 files changed, 250 insertions, 2 deletions
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 72a27ec4b0..17d1f2b289 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -1198,6 +1198,19 @@ static void arm_cpu_finalizefn(Object *obj)
#endif
}
+void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp)
+{
+ Error *local_err = NULL;
+
+ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+ arm_cpu_sve_finalize(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+}
+
static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
@@ -1254,6 +1267,12 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
return;
}
+ arm_cpu_finalize_features(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
if (arm_feature(env, ARM_FEATURE_AARCH64) &&
cpu->has_vfp != cpu->has_neon) {
/*
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index d844ea21d8..a044d6028b 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -184,8 +184,13 @@ typedef struct {
#ifdef TARGET_AARCH64
# define ARM_MAX_VQ 16
+void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp);
+uint32_t arm_cpu_vq_map_next_smaller(ARMCPU *cpu, uint32_t vq);
#else
# define ARM_MAX_VQ 1
+static inline void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) { }
+static inline uint32_t arm_cpu_vq_map_next_smaller(ARMCPU *cpu, uint32_t vq)
+{ return 0; }
#endif
typedef struct ARMVectorReg {
@@ -918,6 +923,18 @@ struct ARMCPU {
/* Used to set the maximum vector length the cpu will support. */
uint32_t sve_max_vq;
+
+ /*
+ * In sve_vq_map each set bit is a supported vector length of
+ * (bit-number + 1) * 16 bytes, i.e. each bit number + 1 is the vector
+ * length in quadwords.
+ *
+ * While processing properties during initialization, corresponding
+ * sve_vq_init bits are set for bits in sve_vq_map that have been
+ * set by properties.
+ */
+ DECLARE_BITMAP(sve_vq_map, ARM_MAX_VQ);
+ DECLARE_BITMAP(sve_vq_init, ARM_MAX_VQ);
};
void arm_cpu_post_init(Object *obj);
@@ -1837,6 +1854,8 @@ static inline int arm_feature(CPUARMState *env, int feature)
return (env->features & (1ULL << feature)) != 0;
}
+void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp);
+
#if !defined(CONFIG_USER_ONLY)
/* Return true if exception levels below EL3 are in secure state,
* or would be following an exception return to that level.
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 89a8ae77fe..34b0ba2cf6 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -256,6 +256,151 @@ static void aarch64_a72_initfn(Object *obj)
define_arm_cp_regs(cpu, cortex_a72_a57_a53_cp_reginfo);
}
+void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp)
+{
+ /*
+ * If any vector lengths are explicitly enabled with sve<N> properties,
+ * then all other lengths are implicitly disabled. If sve-max-vq is
+ * specified then it is the same as explicitly enabling all lengths
+ * up to and including the specified maximum, which means all larger
+ * lengths will be implicitly disabled. If no sve<N> properties
+ * are enabled and sve-max-vq is not specified, then all lengths not
+ * explicitly disabled will be enabled. Additionally, all power-of-two
+ * vector lengths less than the maximum enabled length will be
+ * automatically enabled and all vector lengths larger than the largest
+ * disabled power-of-two vector length will be automatically disabled.
+ * Errors are generated if the user provided input that interferes with
+ * any of the above. Finally, if SVE is not disabled, then at least one
+ * vector length must be enabled.
+ */
+ DECLARE_BITMAP(tmp, ARM_MAX_VQ);
+ uint32_t vq, max_vq = 0;
+
+ /*
+ * Process explicit sve<N> properties.
+ * From the properties, sve_vq_map<N> implies sve_vq_init<N>.
+ * Check first for any sve<N> enabled.
+ */
+ if (!bitmap_empty(cpu->sve_vq_map, ARM_MAX_VQ)) {
+ max_vq = find_last_bit(cpu->sve_vq_map, ARM_MAX_VQ) + 1;
+
+ if (cpu->sve_max_vq && max_vq > cpu->sve_max_vq) {
+ error_setg(errp, "cannot enable sve%d", max_vq * 128);
+ error_append_hint(errp, "sve%d is larger than the maximum vector "
+ "length, sve-max-vq=%d (%d bits)\n",
+ max_vq * 128, cpu->sve_max_vq,
+ cpu->sve_max_vq * 128);
+ return;
+ }
+
+ /* Propagate enabled bits down through required powers-of-two. */
+ for (vq = pow2floor(max_vq); vq >= 1; vq >>= 1) {
+ if (!test_bit(vq - 1, cpu->sve_vq_init)) {
+ set_bit(vq - 1, cpu->sve_vq_map);
+ }
+ }
+ } else if (cpu->sve_max_vq == 0) {
+ /*
+ * No explicit bits enabled, and no implicit bits from sve-max-vq.
+ */
+ if (!cpu_isar_feature(aa64_sve, cpu)) {
+ /* SVE is disabled and so are all vector lengths. Good. */
+ return;
+ }
+
+ /* Disabling a power-of-two disables all larger lengths. */
+ if (test_bit(0, cpu->sve_vq_init)) {
+ error_setg(errp, "cannot disable sve128");
+ error_append_hint(errp, "Disabling sve128 results in all vector "
+ "lengths being disabled.\n");
+ error_append_hint(errp, "With SVE enabled, at least one vector "
+ "length must be enabled.\n");
+ return;
+ }
+ for (vq = 2; vq <= ARM_MAX_VQ; vq <<= 1) {
+ if (test_bit(vq - 1, cpu->sve_vq_init)) {
+ break;
+ }
+ }
+ max_vq = vq <= ARM_MAX_VQ ? vq - 1 : ARM_MAX_VQ;
+
+ bitmap_complement(cpu->sve_vq_map, cpu->sve_vq_init, max_vq);
+ max_vq = find_last_bit(cpu->sve_vq_map, max_vq) + 1;
+ }
+
+ /*
+ * Process the sve-max-vq property.
+ * Note that we know from the above that no bit above
+ * sve-max-vq is currently set.
+ */
+ if (cpu->sve_max_vq != 0) {
+ max_vq = cpu->sve_max_vq;
+
+ if (!test_bit(max_vq - 1, cpu->sve_vq_map) &&
+ test_bit(max_vq - 1, cpu->sve_vq_init)) {
+ error_setg(errp, "cannot disable sve%d", max_vq * 128);
+ error_append_hint(errp, "The maximum vector length must be "
+ "enabled, sve-max-vq=%d (%d bits)\n",
+ max_vq, max_vq * 128);
+ return;
+ }
+
+ /* Set all bits not explicitly set within sve-max-vq. */
+ bitmap_complement(tmp, cpu->sve_vq_init, max_vq);
+ bitmap_or(cpu->sve_vq_map, cpu->sve_vq_map, tmp, max_vq);
+ }
+
+ /*
+ * We should know what max-vq is now. Also, as we're done
+ * manipulating sve-vq-map, we ensure any bits above max-vq
+ * are clear, just in case anybody looks.
+ */
+ assert(max_vq != 0);
+ bitmap_clear(cpu->sve_vq_map, max_vq, ARM_MAX_VQ - max_vq);
+
+ /* Ensure all required powers-of-two are enabled. */
+ for (vq = pow2floor(max_vq); vq >= 1; vq >>= 1) {
+ if (!test_bit(vq - 1, cpu->sve_vq_map)) {
+ error_setg(errp, "cannot disable sve%d", vq * 128);
+ error_append_hint(errp, "sve%d is required as it "
+ "is a power-of-two length smaller than "
+ "the maximum, sve%d\n",
+ vq * 128, max_vq * 128);
+ return;
+ }
+ }
+
+ /*
+ * Now that we validated all our vector lengths, the only question
+ * left to answer is if we even want SVE at all.
+ */
+ if (!cpu_isar_feature(aa64_sve, cpu)) {
+ error_setg(errp, "cannot enable sve%d", max_vq * 128);
+ error_append_hint(errp, "SVE must be enabled to enable vector "
+ "lengths.\n");
+ error_append_hint(errp, "Add sve=on to the CPU property list.\n");
+ return;
+ }
+
+ /* From now on sve_max_vq is the actual maximum supported length. */
+ cpu->sve_max_vq = max_vq;
+}
+
+uint32_t arm_cpu_vq_map_next_smaller(ARMCPU *cpu, uint32_t vq)
+{
+ uint32_t bitnum;
+
+ /*
+ * We allow vq == ARM_MAX_VQ + 1 to be input because the caller may want
+ * to find the maximum vq enabled, which may be ARM_MAX_VQ, but this
+ * function always returns the next smaller than the input.
+ */
+ assert(vq && vq <= ARM_MAX_VQ + 1);
+
+ bitnum = find_last_bit(cpu->sve_vq_map, vq - 1);
+ return bitnum == vq - 1 ? 0 : bitnum + 1;
+}
+
static void cpu_max_get_sve_max_vq(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
@@ -287,6 +432,44 @@ static void cpu_max_set_sve_max_vq(Object *obj, Visitor *v, const char *name,
error_propagate(errp, err);
}
+static void cpu_arm_get_sve_vq(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint32_t vq = atoi(&name[3]) / 128;
+ bool value;
+
+ /* All vector lengths are disabled when SVE is off. */
+ if (!cpu_isar_feature(aa64_sve, cpu)) {
+ value = false;
+ } else {
+ value = test_bit(vq - 1, cpu->sve_vq_map);
+ }
+ visit_type_bool(v, name, &value, errp);
+}
+
+static void cpu_arm_set_sve_vq(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ uint32_t vq = atoi(&name[3]) / 128;
+ Error *err = NULL;
+ bool value;
+
+ visit_type_bool(v, name, &value, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (value) {
+ set_bit(vq - 1, cpu->sve_vq_map);
+ } else {
+ clear_bit(vq - 1, cpu->sve_vq_map);
+ }
+ set_bit(vq - 1, cpu->sve_vq_init);
+}
+
static void cpu_arm_get_sve(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
@@ -323,6 +506,7 @@ static void cpu_arm_set_sve(Object *obj, Visitor *v, const char *name,
static void aarch64_max_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
+ uint32_t vq;
if (kvm_enabled()) {
kvm_arm_set_cpu_features_from_host(cpu);
@@ -426,11 +610,17 @@ static void aarch64_max_initfn(Object *obj)
cpu->dcz_blocksize = 7; /* 512 bytes */
#endif
- cpu->sve_max_vq = ARM_MAX_VQ;
object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq,
cpu_max_set_sve_max_vq, NULL, NULL, &error_fatal);
object_property_add(obj, "sve", "bool", cpu_arm_get_sve,
cpu_arm_set_sve, NULL, NULL, &error_fatal);
+
+ for (vq = 1; vq <= ARM_MAX_VQ; ++vq) {
+ char name[8];
+ sprintf(name, "sve%d", vq * 128);
+ object_property_add(obj, name, "bool", cpu_arm_get_sve_vq,
+ cpu_arm_set_sve_vq, NULL, NULL, &error_fatal);
+ }
}
}
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 63815fc4cf..be67e2c66d 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -5361,6 +5361,13 @@ int sve_exception_el(CPUARMState *env, int el)
return 0;
}
+static uint32_t sve_zcr_get_valid_len(ARMCPU *cpu, uint32_t start_len)
+{
+ uint32_t start_vq = (start_len & 0xf) + 1;
+
+ return arm_cpu_vq_map_next_smaller(cpu, start_vq + 1) - 1;
+}
+
/*
* Given that SVE is enabled, return the vector length for EL.
*/
@@ -5378,7 +5385,8 @@ uint32_t sve_zcr_len_for_el(CPUARMState *env, int el)
if (arm_feature(env, ARM_FEATURE_EL3)) {
zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[3]);
}
- return zcr_len;
+
+ return sve_zcr_get_valid_len(cpu, zcr_len);
}
static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
diff --git a/target/arm/monitor.c b/target/arm/monitor.c
index 2209b27b9a..fa054f8a36 100644
--- a/target/arm/monitor.c
+++ b/target/arm/monitor.c
@@ -90,6 +90,8 @@ GICCapabilityList *qmp_query_gic_capabilities(Error **errp)
return head;
}
+QEMU_BUILD_BUG_ON(ARM_MAX_VQ > 16);
+
/*
* These are cpu model features we want to advertise. The order here
* matters as this is the order in which qmp_query_cpu_model_expansion
@@ -98,6 +100,9 @@ GICCapabilityList *qmp_query_gic_capabilities(Error **errp)
*/
static const char *cpu_model_advertised_features[] = {
"aarch64", "pmu", "sve",
+ "sve128", "sve256", "sve384", "sve512",
+ "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280",
+ "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048",
NULL
};
@@ -186,6 +191,9 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
if (!err) {
visit_check_struct(visitor, &err);
}
+ if (!err) {
+ arm_cpu_finalize_features(ARM_CPU(obj), &err);
+ }
visit_end_struct(visitor, NULL);
visit_free(visitor);
if (err) {
@@ -193,6 +201,10 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
error_propagate(errp, err);
return NULL;
}
+ } else {
+ Error *err = NULL;
+ arm_cpu_finalize_features(ARM_CPU(obj), &err);
+ assert(err == NULL);
}
expansion_info = g_new0(CpuModelExpansionInfo, 1);