feat(cpufeat): extend check_feature() to deal with min/max

So far the check_feature() function compares the subfield of a CPU ID
register against 0, to learn if a feature is enabled or not.
This is problematic for checks that require a certain revision of a
feature, so we should check against a minimum version number instead.
On top of that we might need to add code to support newer versions of a
feature, so we should be alerted if new hardware introduces a higher
number.

Extend the check_feature() function to take two extra arguments: the
minimum version, and the greatest currently known number.
Then make sure that the CPU ID field is in this range.

Change-Id: I425b68535a2ba9eafd31854e74d142183b521cd5
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
This commit is contained in:
Andre Przywara 2023-02-01 11:46:31 +00:00
parent fd1dd4cb2c
commit a4cccb4f6c

View file

@ -36,19 +36,28 @@ static inline void feature_panic(char *feat_name)
/*******************************************************************************
* Function : check_feature
* Check for a valid combination of build time flags (ENABLE_FEAT_xxx) and
* feature availability on the hardware.
* Panics if a feature is forcefully enabled, but not available on the PE.
* feature availability on the hardware. <min> is the smallest feature
* ID field value that is required for that feature.
* Triggers a panic later if a feature is forcefully enabled, but not
* available on the PE. Also will panic if the hardware feature ID field
* is larger than the maximum known and supported number, specified by <max>.
*
* We force inlining here to let the compiler optimise away the whole check
* if the feature is disabled at build time (FEAT_STATE_DISABLED).
******************************************************************************/
static inline void __attribute((__always_inline__))
check_feature(int state, unsigned long field, const char *feat_name)
check_feature(int state, unsigned long field, const char *feat_name,
unsigned int min, unsigned int max)
{
if (state == FEAT_STATE_ALWAYS && field == 0U) {
if (state == FEAT_STATE_ALWAYS && field < min) {
ERROR("FEAT_%s not supported by the PE\n", feat_name);
tainted = true;
}
if (state >= FEAT_STATE_ALWAYS && field > max) {
ERROR("FEAT_%s is version %ld, but is only known up to version %d\n",
feat_name, field, max);
tainted = true;
}
}
/******************************************
@ -312,7 +321,8 @@ void detect_arch_features(void)
/* v8.4 features */
read_feat_dit();
check_feature(ENABLE_FEAT_AMUv1, read_feat_amu_id_field(), "AMUv1");
check_feature(ENABLE_FEAT_AMUv1, read_feat_amu_id_field(),
"AMUv1", 1, 2);
read_feat_mpam();
read_feat_nv2();
read_feat_sel2();
@ -326,12 +336,12 @@ void detect_arch_features(void)
/* v8.6 features */
read_feat_amuv1p1();
check_feature(ENABLE_FEAT_FGT, read_feat_fgt_id_field(), "FGT");
check_feature(ENABLE_FEAT_FGT, read_feat_fgt_id_field(), "FGT", 1, 1);
read_feat_ecv();
read_feat_twed();
/* v8.7 features */
check_feature(ENABLE_FEAT_HCX, read_feat_hcx_id_field(), "HCX");
check_feature(ENABLE_FEAT_HCX, read_feat_hcx_id_field(), "HCX", 1, 1);
/* v9.0 features */
read_feat_brbe();