#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <machine/cpu.h>
#include <machine/cpu_feat.h>
SYSCTL_NODE(_hw, OID_AUTO, feat, CTLFLAG_RD, 0, "CPU features/errata");
static cpu_feat_errata_check_fn cpu_feat_check_cb = NULL;
void
enable_cpu_feat(uint32_t stage)
{
char tunable[32];
struct cpu_feat **featp, *feat;
uint32_t midr;
u_int errata_count, *errata_list;
cpu_feat_errata errata_status;
cpu_feat_en check_status;
bool val;
MPASS((stage & ~CPU_FEAT_STAGE_MASK) == 0);
midr = get_midr();
SET_FOREACH(featp, cpu_feat_set) {
feat = *featp;
if (stage == CPU_FEAT_EARLY_BOOT && PCPU_GET(cpuid) == 0) {
snprintf(tunable, sizeof(tunable), "hw.feat.%s",
feat->feat_name);
if (TUNABLE_BOOL_FETCH(tunable, &val)) {
if (val) {
feat->feat_flags |=
CPU_FEAT_USER_ENABLED;
} else {
feat->feat_flags |=
CPU_FEAT_USER_DISABLED;
}
}
}
if ((feat->feat_flags & CPU_FEAT_STAGE_MASK) != stage)
continue;
if ((feat->feat_flags & CPU_FEAT_SCOPE_MASK)==CPU_FEAT_SYSTEM &&
PCPU_GET(cpuid) != 0)
continue;
if (feat->feat_check != NULL) {
check_status = feat->feat_check(feat, midr);
} else {
check_status = FEAT_DEFAULT_ENABLE;
}
if (check_status == FEAT_ALWAYS_DISABLE)
goto next;
if (check_status != FEAT_ALWAYS_ENABLE) {
if ((feat->feat_flags & CPU_FEAT_USER_DISABLED) != 0)
goto next;
if (check_status == FEAT_DEFAULT_DISABLE &&
(feat->feat_flags & CPU_FEAT_USER_ENABLED) == 0)
goto next;
}
errata_status = ERRATA_NONE;
errata_list = NULL;
errata_count = 0;
if (feat->feat_has_errata != NULL) {
if (feat->feat_has_errata(feat, midr, &errata_list,
&errata_count)) {
errata_status = ERRATA_AFFECTED;
}
}
if (errata_status == ERRATA_AFFECTED &&
cpu_feat_check_cb != NULL) {
for (int i = 0; i < errata_count; i++) {
cpu_feat_errata new_status;
new_status = cpu_feat_check_cb(feat,
errata_list[i]);
if (new_status != ERRATA_UNKNOWN) {
errata_status = new_status;
errata_list = &errata_list[i];
errata_count = 1;
break;
}
}
}
MPASS(errata_status != ERRATA_UNKNOWN);
if (feat->feat_enable(feat, errata_status, errata_list,
errata_count))
feat->feat_enabled = true;
next:
if (!feat->feat_enabled && feat->feat_disabled != NULL)
feat->feat_disabled(feat);
}
}
static void
enable_cpu_feat_after_dev(void *dummy __unused)
{
MPASS(PCPU_GET(cpuid) == 0);
enable_cpu_feat(CPU_FEAT_AFTER_DEV);
}
SYSINIT(enable_cpu_feat_after_dev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE,
enable_cpu_feat_after_dev, NULL);
void
cpu_feat_register_errata_check(cpu_feat_errata_check_fn cb)
{
MPASS(cpu_feat_check_cb == NULL);
cpu_feat_check_cb = cb;
}