#include "kstat.h"
#include "xcall.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/sysctl.h>
#include <sys/task.h>
#include <sys/user.h>
#include <sys/kstat.h>
#include <uvm/uvm_extern.h>
#include <machine/fdt.h>
#include <machine/elf.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_regulator.h>
#include <dev/ofw/ofw_thermal.h>
#include <dev/ofw/fdt.h>
#include <machine/cpufunc.h>
#include "psci.h"
#if NPSCI > 0
#include <dev/fdt/pscivar.h>
#endif
#define id_aa64zfr0_el1 s3_0_c0_c4_4
#define zcr_el1 s3_0_c1_c2_0
#define CPU_IMPL_ARM 0x41
#define CPU_IMPL_CAVIUM 0x43
#define CPU_IMPL_AMCC 0x50
#define CPU_IMPL_QCOM 0x51
#define CPU_IMPL_APPLE 0x61
#define CPU_IMPL_AMPERE 0xc0
#define CPU_PART_CORTEX_A34 0xd02
#define CPU_PART_CORTEX_A53 0xd03
#define CPU_PART_CORTEX_A35 0xd04
#define CPU_PART_CORTEX_A55 0xd05
#define CPU_PART_CORTEX_A65 0xd06
#define CPU_PART_CORTEX_A57 0xd07
#define CPU_PART_CORTEX_A72 0xd08
#define CPU_PART_CORTEX_A73 0xd09
#define CPU_PART_CORTEX_A75 0xd0a
#define CPU_PART_CORTEX_A76 0xd0b
#define CPU_PART_NEOVERSE_N1 0xd0c
#define CPU_PART_CORTEX_A77 0xd0d
#define CPU_PART_CORTEX_A76AE 0xd0e
#define CPU_PART_NEOVERSE_V1 0xd40
#define CPU_PART_CORTEX_A78 0xd41
#define CPU_PART_CORTEX_A78AE 0xd42
#define CPU_PART_CORTEX_A65AE 0xd43
#define CPU_PART_CORTEX_X1 0xd44
#define CPU_PART_CORTEX_A510 0xd46
#define CPU_PART_CORTEX_A710 0xd47
#define CPU_PART_CORTEX_X2 0xd48
#define CPU_PART_NEOVERSE_N2 0xd49
#define CPU_PART_NEOVERSE_E1 0xd4a
#define CPU_PART_CORTEX_A78C 0xd4b
#define CPU_PART_CORTEX_X1C 0xd4c
#define CPU_PART_CORTEX_A715 0xd4d
#define CPU_PART_CORTEX_X3 0xd4e
#define CPU_PART_NEOVERSE_V2 0xd4f
#define CPU_PART_CORTEX_A520 0xd80
#define CPU_PART_CORTEX_A720 0xd81
#define CPU_PART_CORTEX_X4 0xd82
#define CPU_PART_NEOVERSE_V3AE 0xd83
#define CPU_PART_NEOVERSE_V3 0xd84
#define CPU_PART_CORTEX_X925 0xd85
#define CPU_PART_CORTEX_A725 0xd87
#define CPU_PART_CORTEX_A520AE 0xd88
#define CPU_PART_CORTEX_A720AE 0xd89
#define CPU_PART_C1_NANO 0xd8a
#define CPU_PART_C1_PRO 0xd8b
#define CPU_PART_C1_ULTRA 0xd8c
#define CPU_PART_NEOVERSE_N3 0xd8e
#define CPU_PART_CORTEX_A320 0xd8f
#define CPU_PART_C1_PREMIUM 0xd90
#define CPU_PART_THUNDERX_T88 0x0a1
#define CPU_PART_THUNDERX_T81 0x0a2
#define CPU_PART_THUNDERX_T83 0x0a3
#define CPU_PART_THUNDERX2_T99 0x0af
#define CPU_PART_X_GENE 0x000
#define CPU_PART_ORYON 0x001
#define CPU_PART_KRYO400_GOLD 0x804
#define CPU_PART_KRYO400_SILVER 0x805
#define CPU_PART_ICESTORM 0x022
#define CPU_PART_FIRESTORM 0x023
#define CPU_PART_ICESTORM_PRO 0x024
#define CPU_PART_FIRESTORM_PRO 0x025
#define CPU_PART_ICESTORM_MAX 0x028
#define CPU_PART_FIRESTORM_MAX 0x029
#define CPU_PART_BLIZZARD 0x032
#define CPU_PART_AVALANCHE 0x033
#define CPU_PART_BLIZZARD_PRO 0x034
#define CPU_PART_AVALANCHE_PRO 0x035
#define CPU_PART_BLIZZARD_MAX 0x038
#define CPU_PART_AVALANCHE_MAX 0x039
#define CPU_PART_AMPERE1 0xac3
#define CPU_IMPL(midr) (((midr) >> 24) & 0xff)
#define CPU_PART(midr) (((midr) >> 4) & 0xfff)
#define CPU_VAR(midr) (((midr) >> 20) & 0xf)
#define CPU_REV(midr) (((midr) >> 0) & 0xf)
struct cpu_cores {
int id;
char *name;
};
struct cpu_cores cpu_cores_none[] = {
{ 0, NULL },
};
struct cpu_cores cpu_cores_arm[] = {
{ CPU_PART_C1_NANO, "C1-Nano" },
{ CPU_PART_C1_PREMIUM, "C1-Premium" },
{ CPU_PART_C1_PRO, "C1-Pro" },
{ CPU_PART_C1_ULTRA, "C1-Ultra" },
{ CPU_PART_CORTEX_A34, "Cortex-A34" },
{ CPU_PART_CORTEX_A35, "Cortex-A35" },
{ CPU_PART_CORTEX_A53, "Cortex-A53" },
{ CPU_PART_CORTEX_A55, "Cortex-A55" },
{ CPU_PART_CORTEX_A57, "Cortex-A57" },
{ CPU_PART_CORTEX_A65, "Cortex-A65" },
{ CPU_PART_CORTEX_A65AE, "Cortex-A65AE" },
{ CPU_PART_CORTEX_A72, "Cortex-A72" },
{ CPU_PART_CORTEX_A73, "Cortex-A73" },
{ CPU_PART_CORTEX_A75, "Cortex-A75" },
{ CPU_PART_CORTEX_A76, "Cortex-A76" },
{ CPU_PART_CORTEX_A76AE, "Cortex-A76AE" },
{ CPU_PART_CORTEX_A77, "Cortex-A77" },
{ CPU_PART_CORTEX_A78, "Cortex-A78" },
{ CPU_PART_CORTEX_A78AE, "Cortex-A78AE" },
{ CPU_PART_CORTEX_A78C, "Cortex-A78C" },
{ CPU_PART_CORTEX_A320, "Cortex-A320" },
{ CPU_PART_CORTEX_A510, "Cortex-A510" },
{ CPU_PART_CORTEX_A520, "Cortex-A520" },
{ CPU_PART_CORTEX_A520AE, "Cortex-A520AE" },
{ CPU_PART_CORTEX_A710, "Cortex-A710" },
{ CPU_PART_CORTEX_A715, "Cortex-A715" },
{ CPU_PART_CORTEX_A720, "Cortex-A720" },
{ CPU_PART_CORTEX_A720AE, "Cortex-A720AE" },
{ CPU_PART_CORTEX_A725, "Cortex-A725" },
{ CPU_PART_CORTEX_X1, "Cortex-X1" },
{ CPU_PART_CORTEX_X1C, "Cortex-X1C" },
{ CPU_PART_CORTEX_X2, "Cortex-X2" },
{ CPU_PART_CORTEX_X3, "Cortex-X3" },
{ CPU_PART_CORTEX_X4, "Cortex-X4" },
{ CPU_PART_CORTEX_X925, "Cortex-X925" },
{ CPU_PART_NEOVERSE_E1, "Neoverse E1" },
{ CPU_PART_NEOVERSE_N1, "Neoverse N1" },
{ CPU_PART_NEOVERSE_N2, "Neoverse N2" },
{ CPU_PART_NEOVERSE_N3, "Neoverse N3" },
{ CPU_PART_NEOVERSE_V1, "Neoverse V1" },
{ CPU_PART_NEOVERSE_V2, "Neoverse V2" },
{ CPU_PART_NEOVERSE_V3, "Neoverse V3" },
{ CPU_PART_NEOVERSE_V3AE, "Neoverse V3AE" },
{ 0, NULL },
};
struct cpu_cores cpu_cores_cavium[] = {
{ CPU_PART_THUNDERX_T88, "ThunderX T88" },
{ CPU_PART_THUNDERX_T81, "ThunderX T81" },
{ CPU_PART_THUNDERX_T83, "ThunderX T83" },
{ CPU_PART_THUNDERX2_T99, "ThunderX2 T99" },
{ 0, NULL },
};
struct cpu_cores cpu_cores_amcc[] = {
{ CPU_PART_X_GENE, "X-Gene" },
{ 0, NULL },
};
struct cpu_cores cpu_cores_qcom[] = {
{ CPU_PART_KRYO400_GOLD, "Kryo 400 Gold" },
{ CPU_PART_KRYO400_SILVER, "Kryo 400 Silver" },
{ CPU_PART_ORYON, "Oryon" },
{ 0, NULL },
};
struct cpu_cores cpu_cores_apple[] = {
{ CPU_PART_ICESTORM, "Icestorm" },
{ CPU_PART_FIRESTORM, "Firestorm" },
{ CPU_PART_ICESTORM_PRO, "Icestorm Pro" },
{ CPU_PART_FIRESTORM_PRO, "Firestorm Pro" },
{ CPU_PART_ICESTORM_MAX, "Icestorm Max" },
{ CPU_PART_FIRESTORM_MAX, "Firestorm Max" },
{ CPU_PART_BLIZZARD, "Blizzard" },
{ CPU_PART_AVALANCHE, "Avalanche" },
{ CPU_PART_BLIZZARD_PRO, "Blizzard Pro" },
{ CPU_PART_AVALANCHE_PRO, "Avalanche Pro" },
{ CPU_PART_BLIZZARD_MAX, "Blizzard Max" },
{ CPU_PART_AVALANCHE_MAX, "Avalanche Max" },
{ 0, NULL },
};
struct cpu_cores cpu_cores_ampere[] = {
{ CPU_PART_AMPERE1, "AmpereOne" },
{ 0, NULL },
};
const struct implementers {
int id;
char *name;
struct cpu_cores *corelist;
} cpu_implementers[] = {
{ CPU_IMPL_ARM, "ARM", cpu_cores_arm },
{ CPU_IMPL_CAVIUM, "Cavium", cpu_cores_cavium },
{ CPU_IMPL_AMCC, "Applied Micro", cpu_cores_amcc },
{ CPU_IMPL_QCOM, "Qualcomm", cpu_cores_qcom },
{ CPU_IMPL_APPLE, "Apple", cpu_cores_apple },
{ CPU_IMPL_AMPERE, "Ampere", cpu_cores_ampere },
{ 0, NULL },
};
char cpu_model[64];
int cpu_node;
uint64_t cpu_id_aa64isar0;
uint64_t cpu_id_aa64isar1;
uint64_t cpu_id_aa64isar2;
uint64_t cpu_id_aa64mmfr0;
uint64_t cpu_id_aa64mmfr1;
uint64_t cpu_id_aa64mmfr2;
uint64_t cpu_id_aa64pfr0;
uint64_t cpu_id_aa64pfr1;
uint64_t cpu_id_aa64zfr0;
int arm64_has_lse;
int arm64_has_rng;
#ifdef CRYPTO
int arm64_has_aes;
#endif
struct opp {
uint64_t opp_hz;
uint32_t opp_microvolt;
};
struct opp_table {
LIST_ENTRY(opp_table) ot_list;
uint32_t ot_phandle;
struct opp *ot_opp;
u_int ot_nopp;
uint64_t ot_opp_hz_min;
uint64_t ot_opp_hz_max;
struct cpu_info *ot_master;
};
extern char trampoline_vectors_none[];
extern char trampoline_vectors_loop_8[];
extern char trampoline_vectors_loop_11[];
extern char trampoline_vectors_loop_24[];
extern char trampoline_vectors_loop_32[];
extern char trampoline_vectors_loop_132[];
#if NPSCI > 0
extern char trampoline_vectors_psci_hvc[];
extern char trampoline_vectors_psci_smc[];
#endif
extern char trampoline_vectors_clrbhb[];
struct cpu_info *cpu_info_list = &cpu_info_primary;
int cpu_match(struct device *, void *, void *);
void cpu_attach(struct device *, struct device *, void *);
const struct cfattach cpu_ca = {
sizeof(struct device), cpu_match, cpu_attach
};
struct cfdriver cpu_cd = {
NULL, "cpu", DV_DULL
};
struct timeout cpu_rng_to;
void cpu_rng(void *);
void cpu_opp_init(struct cpu_info *, uint32_t);
void cpu_psci_init(struct cpu_info *);
void cpu_psci_idle_cycle(void);
void cpu_flush_bp_noop(void);
void cpu_flush_bp_psci(void);
void cpu_serror_apple(void);
#if NKSTAT > 0
void cpu_kstat_attach(struct cpu_info *ci);
void cpu_opp_kstat_attach(struct cpu_info *ci);
#endif
void
cpu_rng(void *arg)
{
struct timeout *to = arg;
uint64_t rndr;
int ret;
ret = __builtin_arm_rndrrs(&rndr);
if (ret)
ret = __builtin_arm_rndr(&rndr);
if (ret == 0) {
enqueue_randomness(rndr & 0xffffffff);
enqueue_randomness(rndr >> 32);
}
if (to)
timeout_add_msec(to, 1000);
}
void
cpu_mitigate_spectre_v2(struct cpu_info *ci)
{
uint64_t id;
ci->ci_flush_bp = cpu_flush_bp_psci;
switch (CPU_IMPL(ci->ci_midr)) {
case CPU_IMPL_ARM:
switch (CPU_PART(ci->ci_midr)) {
case CPU_PART_CORTEX_A35:
case CPU_PART_CORTEX_A53:
case CPU_PART_CORTEX_A55:
ci->ci_flush_bp = cpu_flush_bp_noop;
break;
}
break;
case CPU_IMPL_QCOM:
switch (CPU_PART(ci->ci_midr)) {
case CPU_PART_KRYO400_SILVER:
ci->ci_flush_bp = cpu_flush_bp_noop;
break;
}
}
id = READ_SPECIALREG(id_aa64pfr0_el1);
if (ID_AA64PFR0_CSV2(id) >= ID_AA64PFR0_CSV2_IMPL)
ci->ci_flush_bp = cpu_flush_bp_noop;
}
void
cpu_mitigate_spectre_bhb(struct cpu_info *ci)
{
uint64_t id;
switch (CPU_IMPL(ci->ci_midr)) {
case CPU_IMPL_ARM:
switch (CPU_PART(ci->ci_midr)) {
case CPU_PART_CORTEX_A57:
case CPU_PART_CORTEX_A72:
ci->ci_trampoline_vectors =
(vaddr_t)trampoline_vectors_loop_8;
break;
case CPU_PART_CORTEX_A76:
case CPU_PART_CORTEX_A76AE:
case CPU_PART_CORTEX_A77:
case CPU_PART_NEOVERSE_N1:
ci->ci_trampoline_vectors =
(vaddr_t)trampoline_vectors_loop_24;
break;
case CPU_PART_CORTEX_A78:
case CPU_PART_CORTEX_A78AE:
case CPU_PART_CORTEX_A78C:
case CPU_PART_CORTEX_X1:
case CPU_PART_CORTEX_X1C:
case CPU_PART_CORTEX_X2:
case CPU_PART_CORTEX_A710:
case CPU_PART_NEOVERSE_N2:
case CPU_PART_NEOVERSE_V1:
ci->ci_trampoline_vectors =
(vaddr_t)trampoline_vectors_loop_32;
break;
case CPU_PART_CORTEX_X3:
case CPU_PART_CORTEX_X4:
case CPU_PART_CORTEX_X925:
case CPU_PART_NEOVERSE_V2:
case CPU_PART_NEOVERSE_V3:
case CPU_PART_NEOVERSE_V3AE:
ci->ci_trampoline_vectors =
(vaddr_t)trampoline_vectors_loop_132;
break;
}
break;
case CPU_IMPL_AMPERE:
switch (CPU_PART(ci->ci_midr)) {
case CPU_PART_AMPERE1:
ci->ci_trampoline_vectors =
(vaddr_t)trampoline_vectors_loop_11;
break;
}
break;
}
#if NPSCI > 0
if (ci->ci_trampoline_vectors == (vaddr_t)trampoline_vectors_none &&
smccc_needs_arch_workaround_3()) {
ci->ci_flush_bp = cpu_flush_bp_noop;
if (psci_method() == PSCI_METHOD_HVC)
ci->ci_trampoline_vectors =
(vaddr_t)trampoline_vectors_psci_hvc;
if (psci_method() == PSCI_METHOD_SMC)
ci->ci_trampoline_vectors =
(vaddr_t)trampoline_vectors_psci_smc;
}
#endif
id = READ_SPECIALREG(id_aa64isar2_el1);
if (ID_AA64ISAR2_CLRBHB(id) >= ID_AA64ISAR2_CLRBHB_IMPL)
ci->ci_trampoline_vectors = (vaddr_t)trampoline_vectors_clrbhb;
id = READ_SPECIALREG(id_aa64mmfr1_el1);
if (ID_AA64MMFR1_ECBHB(id) >= ID_AA64MMFR1_ECBHB_IMPL)
ci->ci_trampoline_vectors = (vaddr_t)trampoline_vectors_none;
id = READ_SPECIALREG(id_aa64pfr0_el1);
if (ID_AA64PFR0_CSV2(id) >= ID_AA64PFR0_CSV2_HCXT)
ci->ci_trampoline_vectors = (vaddr_t)trampoline_vectors_none;
}
void
cpu_mitigate_spectre_v4(struct cpu_info *ci)
{
uint64_t id;
switch (CPU_IMPL(ci->ci_midr)) {
case CPU_IMPL_ARM:
switch (CPU_PART(ci->ci_midr)) {
case CPU_PART_CORTEX_A35:
case CPU_PART_CORTEX_A53:
case CPU_PART_CORTEX_A55:
return;
}
break;
case CPU_IMPL_QCOM:
switch (CPU_PART(ci->ci_midr)) {
case CPU_PART_KRYO400_SILVER:
return;
}
break;
}
id = READ_SPECIALREG(id_aa64pfr1_el1);
if (ID_AA64PFR1_SSBS(id) >= ID_AA64PFR1_SSBS_PSTATE)
return;
smccc_enable_arch_workaround_2();
}
void
cpu_identify(struct cpu_info *ci)
{
static uint64_t prev_id_aa64isar0;
static uint64_t prev_id_aa64isar1;
static uint64_t prev_id_aa64isar2;
static uint64_t prev_id_aa64mmfr0;
static uint64_t prev_id_aa64mmfr1;
static uint64_t prev_id_aa64mmfr2;
static uint64_t prev_id_aa64pfr0;
static uint64_t prev_id_aa64pfr1;
static uint64_t prev_id_aa64zfr0;
uint64_t midr, impl, part;
uint64_t clidr, ccsidr, id;
uint32_t ctr, sets, ways, line;
const char *impl_name = NULL;
const char *part_name = NULL;
const char *il1p_name = NULL;
const char *sep;
struct cpu_cores *coreselecter = cpu_cores_none;
int ccidx;
int i;
midr = READ_SPECIALREG(midr_el1);
impl = CPU_IMPL(midr);
part = CPU_PART(midr);
ci->ci_midr = midr;
for (i = 0; cpu_implementers[i].name; i++) {
if (impl == cpu_implementers[i].id) {
impl_name = cpu_implementers[i].name;
coreselecter = cpu_implementers[i].corelist;
break;
}
}
for (i = 0; coreselecter[i].name; i++) {
if (part == coreselecter[i].id) {
part_name = coreselecter[i].name;
break;
}
}
if (impl_name && part_name) {
printf(" %s %s r%llup%llu", impl_name, part_name, CPU_VAR(midr),
CPU_REV(midr));
if (CPU_IS_PRIMARY(ci))
snprintf(cpu_model, sizeof(cpu_model),
"%s %s r%llup%llu", impl_name, part_name,
CPU_VAR(midr), CPU_REV(midr));
} else {
printf(" Unknown, MIDR 0x%llx", midr);
if (CPU_IS_PRIMARY(ci))
snprintf(cpu_model, sizeof(cpu_model), "Unknown");
}
ctr = READ_SPECIALREG(ctr_el0);
switch (ctr & CTR_IL1P_MASK) {
case CTR_IL1P_AIVIVT:
il1p_name = "AIVIVT ";
break;
case CTR_IL1P_VIPT:
il1p_name = "VIPT ";
break;
case CTR_IL1P_PIPT:
il1p_name = "PIPT ";
break;
}
id = READ_SPECIALREG(id_aa64mmfr2_el1);
clidr = READ_SPECIALREG(clidr_el1);
if (ID_AA64MMFR2_CCIDX(id) > ID_AA64MMFR2_CCIDX_IMPL) {
clidr = 0;
} else if (ID_AA64MMFR2_CCIDX(id) == ID_AA64MMFR2_CCIDX_IMPL) {
ccidx = 1;
} else {
ccidx = 0;
}
for (i = 0; i < 7; i++) {
if ((clidr & CLIDR_CTYPE_MASK) == 0)
break;
printf("\n%s:", ci->ci_dev->dv_xname);
sep = "";
if (clidr & CLIDR_CTYPE_INSN) {
WRITE_SPECIALREG(csselr_el1,
i << CSSELR_LEVEL_SHIFT | CSSELR_IND);
__asm volatile("isb");
ccsidr = READ_SPECIALREG(ccsidr_el1);
if (ccidx) {
sets = CCSIDR_CCIDX_SETS(ccsidr);
ways = CCSIDR_CCIDX_WAYS(ccsidr);
line = CCSIDR_CCIDX_LINE_SIZE(ccsidr);
} else {
sets = CCSIDR_SETS(ccsidr);
ways = CCSIDR_WAYS(ccsidr);
line = CCSIDR_LINE_SIZE(ccsidr);
}
printf("%s %dKB %db/line %d-way L%d %sI-cache", sep,
(sets * ways * line) / 1024, line, ways, (i + 1),
il1p_name);
il1p_name = "";
sep = ",";
}
if (clidr & CLIDR_CTYPE_DATA) {
WRITE_SPECIALREG(csselr_el1, i << CSSELR_LEVEL_SHIFT);
__asm volatile("isb");
ccsidr = READ_SPECIALREG(ccsidr_el1);
if (ccidx) {
sets = CCSIDR_CCIDX_SETS(ccsidr);
ways = CCSIDR_CCIDX_WAYS(ccsidr);
line = CCSIDR_CCIDX_LINE_SIZE(ccsidr);
} else {
sets = CCSIDR_SETS(ccsidr);
ways = CCSIDR_WAYS(ccsidr);
line = CCSIDR_LINE_SIZE(ccsidr);
}
printf("%s %dKB %db/line %d-way L%d D-cache", sep,
(sets * ways * line) / 1024, line, ways, (i + 1));
sep = ",";
}
if (clidr & CLIDR_CTYPE_UNIFIED) {
WRITE_SPECIALREG(csselr_el1, i << CSSELR_LEVEL_SHIFT);
__asm volatile("isb");
ccsidr = READ_SPECIALREG(ccsidr_el1);
if (ccidx) {
sets = CCSIDR_CCIDX_SETS(ccsidr);
ways = CCSIDR_CCIDX_WAYS(ccsidr);
line = CCSIDR_CCIDX_LINE_SIZE(ccsidr);
} else {
sets = CCSIDR_SETS(ccsidr);
ways = CCSIDR_WAYS(ccsidr);
line = CCSIDR_LINE_SIZE(ccsidr);
}
printf("%s %dKB %db/line %d-way L%d cache", sep,
(sets * ways * line) / 1024, line, ways, (i + 1));
}
clidr >>= 3;
}
cpu_mitigate_spectre_v2(ci);
cpu_mitigate_spectre_bhb(ci);
cpu_mitigate_spectre_v4(ci);
if (impl == CPU_IMPL_APPLE)
ci->ci_serror = cpu_serror_apple;
if (READ_SPECIALREG(id_aa64isar0_el1) == prev_id_aa64isar0 &&
READ_SPECIALREG(id_aa64isar1_el1) == prev_id_aa64isar1 &&
READ_SPECIALREG(id_aa64isar2_el1) == prev_id_aa64isar2 &&
READ_SPECIALREG(id_aa64mmfr0_el1) == prev_id_aa64mmfr0 &&
READ_SPECIALREG(id_aa64mmfr1_el1) == prev_id_aa64mmfr1 &&
READ_SPECIALREG(id_aa64mmfr2_el1) == prev_id_aa64mmfr2 &&
READ_SPECIALREG(id_aa64pfr0_el1) == prev_id_aa64pfr0 &&
READ_SPECIALREG(id_aa64pfr1_el1) == prev_id_aa64pfr1 &&
READ_SPECIALREG(id_aa64zfr0_el1) == prev_id_aa64zfr0)
return;
if (READ_SPECIALREG(id_aa64isar0_el1) != cpu_id_aa64isar0) {
printf("\n%s: mismatched ID_AA64ISAR0_EL1",
ci->ci_dev->dv_xname);
}
if (READ_SPECIALREG(id_aa64isar1_el1) != cpu_id_aa64isar1) {
printf("\n%s: mismatched ID_AA64ISAR1_EL1",
ci->ci_dev->dv_xname);
}
if (READ_SPECIALREG(id_aa64isar2_el1) != cpu_id_aa64isar2) {
printf("\n%s: mismatched ID_AA64ISAR2_EL1",
ci->ci_dev->dv_xname);
}
if (READ_SPECIALREG(id_aa64mmfr0_el1) != cpu_id_aa64mmfr0) {
printf("\n%s: mismatched ID_AA64MMFR0_EL1",
ci->ci_dev->dv_xname);
}
id = READ_SPECIALREG(id_aa64mmfr1_el1);
id &= ~ID_AA64MMFR1_SPECSEI_MASK;
if (id != cpu_id_aa64mmfr1) {
printf("\n%s: mismatched ID_AA64MMFR1_EL1",
ci->ci_dev->dv_xname);
}
if (READ_SPECIALREG(id_aa64mmfr2_el1) != cpu_id_aa64mmfr2) {
printf("\n%s: mismatched ID_AA64MMFR2_EL1",
ci->ci_dev->dv_xname);
}
id = READ_SPECIALREG(id_aa64pfr0_el1);
id &= ~ID_AA64PFR0_CSV2_MASK;
id &= ~ID_AA64PFR0_CSV3_MASK;
id &= ~ID_AA64PFR0_EL0_MASK;
id &= ~ID_AA64PFR0_EL1_MASK;
id &= ~ID_AA64PFR0_EL2_MASK;
id &= ~ID_AA64PFR0_EL3_MASK;
if (id != cpu_id_aa64pfr0) {
printf("\n%s: mismatched ID_AA64PFR0_EL1",
ci->ci_dev->dv_xname);
}
if (READ_SPECIALREG(id_aa64pfr1_el1) != cpu_id_aa64pfr1) {
printf("\n%s: mismatched ID_AA64PFR1_EL1",
ci->ci_dev->dv_xname);
}
printf("\n%s: ", ci->ci_dev->dv_xname);
id = READ_SPECIALREG(id_aa64isar0_el1);
sep = "";
if (ID_AA64ISAR0_RNDR(id) >= ID_AA64ISAR0_RNDR_IMPL) {
printf("%sRNDR", sep);
sep = ",";
arm64_has_rng = 1;
}
if (ID_AA64ISAR0_TLB(id) >= ID_AA64ISAR0_TLB_IOS) {
printf("%sTLBIOS", sep);
sep = ",";
}
if (ID_AA64ISAR0_TLB(id) >= ID_AA64ISAR0_TLB_IRANGE)
printf("+IRANGE");
if (ID_AA64ISAR0_TS(id) >= ID_AA64ISAR0_TS_BASE) {
printf("%sTS", sep);
sep = ",";
}
if (ID_AA64ISAR0_TS(id) >= ID_AA64ISAR0_TS_AXFLAG)
printf("+AXFLAG");
if (ID_AA64ISAR0_FHM(id) >= ID_AA64ISAR0_FHM_IMPL) {
printf("%sFHM", sep);
sep = ",";
}
if (ID_AA64ISAR0_DP(id) >= ID_AA64ISAR0_DP_IMPL) {
printf("%sDP", sep);
sep = ",";
}
if (ID_AA64ISAR0_SM4(id) >= ID_AA64ISAR0_SM4_IMPL) {
printf("%sSM4", sep);
sep = ",";
}
if (ID_AA64ISAR0_SM3(id) >= ID_AA64ISAR0_SM3_IMPL) {
printf("%sSM3", sep);
sep = ",";
}
if (ID_AA64ISAR0_SHA3(id) >= ID_AA64ISAR0_SHA3_IMPL) {
printf("%sSHA3", sep);
sep = ",";
}
if (ID_AA64ISAR0_RDM(id) >= ID_AA64ISAR0_RDM_IMPL) {
printf("%sRDM", sep);
sep = ",";
}
if (ID_AA64ISAR0_ATOMIC(id) >= ID_AA64ISAR0_ATOMIC_IMPL) {
printf("%sAtomic", sep);
sep = ",";
arm64_has_lse = 1;
}
if (ID_AA64ISAR0_CRC32(id) >= ID_AA64ISAR0_CRC32_BASE) {
printf("%sCRC32", sep);
sep = ",";
}
if (ID_AA64ISAR0_SHA2(id) >= ID_AA64ISAR0_SHA2_BASE) {
printf("%sSHA2", sep);
sep = ",";
}
if (ID_AA64ISAR0_SHA2(id) >= ID_AA64ISAR0_SHA2_512)
printf("+SHA512");
if (ID_AA64ISAR0_SHA1(id) >= ID_AA64ISAR0_SHA1_BASE) {
printf("%sSHA1", sep);
sep = ",";
}
if (ID_AA64ISAR0_AES(id) >= ID_AA64ISAR0_AES_BASE) {
printf("%sAES", sep);
sep = ",";
#ifdef CRYPTO
arm64_has_aes = 1;
#endif
}
if (ID_AA64ISAR0_AES(id) >= ID_AA64ISAR0_AES_PMULL)
printf("+PMULL");
id = READ_SPECIALREG(id_aa64isar1_el1);
if (ID_AA64ISAR1_LS64(id) >= ID_AA64ISAR1_LS64_BASE) {
printf("%sLS64", sep);
sep = ",";
}
if (ID_AA64ISAR1_LS64(id) >= ID_AA64ISAR1_LS64_V)
printf("+V");
if (ID_AA64ISAR1_LS64(id) >= ID_AA64ISAR1_LS64_ACCDATA)
printf("+ACCDATA");
if (ID_AA64ISAR1_XS(id) >= ID_AA64ISAR1_XS_IMPL) {
printf("%sXS", sep);
sep = ",";
}
if (ID_AA64ISAR1_I8MM(id) >= ID_AA64ISAR1_I8MM_IMPL) {
printf("%sI8MM", sep);
sep = ",";
}
if (ID_AA64ISAR1_DGH(id) >= ID_AA64ISAR1_DGH_IMPL) {
printf("%sDGH", sep);
sep = ",";
}
if (ID_AA64ISAR1_BF16(id) >= ID_AA64ISAR1_BF16_BASE) {
printf("%sBF16", sep);
sep = ",";
}
if (ID_AA64ISAR1_BF16(id) >= ID_AA64ISAR1_BF16_EBF)
printf("+EBF");
if (ID_AA64ISAR1_SPECRES(id) >= ID_AA64ISAR1_SPECRES_IMPL) {
printf("%sSPECRES", sep);
sep = ",";
}
if (ID_AA64ISAR1_SB(id) >= ID_AA64ISAR1_SB_IMPL) {
printf("%sSB", sep);
sep = ",";
}
if (ID_AA64ISAR1_FRINTTS(id) >= ID_AA64ISAR1_FRINTTS_IMPL) {
printf("%sFRINTTS", sep);
sep = ",";
}
if (ID_AA64ISAR1_GPI(id) >= ID_AA64ISAR1_GPI_IMPL) {
printf("%sGPI", sep);
sep = ",";
}
if (ID_AA64ISAR1_GPA(id) >= ID_AA64ISAR1_GPA_IMPL) {
printf("%sGPA", sep);
sep = ",";
}
if (ID_AA64ISAR1_LRCPC(id) >= ID_AA64ISAR1_LRCPC_BASE) {
printf("%sLRCPC", sep);
sep = ",";
}
if (ID_AA64ISAR1_LRCPC(id) >= ID_AA64ISAR1_LRCPC_LDAPUR)
printf("+LDAPUR");
if (ID_AA64ISAR1_FCMA(id) >= ID_AA64ISAR1_FCMA_IMPL) {
printf("%sFCMA", sep);
sep = ",";
}
if (ID_AA64ISAR1_JSCVT(id) >= ID_AA64ISAR1_JSCVT_IMPL) {
printf("%sJSCVT", sep);
sep = ",";
}
if (ID_AA64ISAR1_API(id) >= ID_AA64ISAR1_API_PAC) {
printf("%sAPI", sep);
sep = ",";
}
if (ID_AA64ISAR1_API(id) == ID_AA64ISAR1_API_EPAC)
printf("+EPAC");
else if (ID_AA64ISAR1_API(id) >= ID_AA64ISAR1_API_EPAC2)
printf("+EPAC2");
if (ID_AA64ISAR1_API(id) >= ID_AA64ISAR1_API_FPAC)
printf("+FPAC");
if (ID_AA64ISAR1_API(id) >= ID_AA64ISAR1_API_FPAC_COMBINED)
printf("+COMBINED");
if (ID_AA64ISAR1_APA(id) >= ID_AA64ISAR1_APA_PAC) {
printf("%sAPA", sep);
sep = ",";
}
if (ID_AA64ISAR1_APA(id) == ID_AA64ISAR1_APA_EPAC)
printf("+EPAC");
else if (ID_AA64ISAR1_APA(id) >= ID_AA64ISAR1_APA_EPAC2)
printf("+EPAC2");
if (ID_AA64ISAR1_APA(id) >= ID_AA64ISAR1_APA_FPAC)
printf("+FPAC");
if (ID_AA64ISAR1_APA(id) >= ID_AA64ISAR1_APA_FPAC_COMBINED)
printf("+COMBINED");
if (ID_AA64ISAR1_DPB(id) >= ID_AA64ISAR1_DPB_IMPL) {
printf("%sDPB", sep);
sep = ",";
}
if (ID_AA64ISAR1_DPB(id) >= ID_AA64ISAR1_DPB_DCCVADP)
printf("+DCCVADP");
id = READ_SPECIALREG(id_aa64isar2_el1);
if (ID_AA64ISAR2_CSSC(id) >= ID_AA64ISAR2_CSSC_IMPL) {
printf("%sCSSC", sep);
sep = ",";
}
if (ID_AA64ISAR2_RPRFM(id) >= ID_AA64ISAR2_RPRFM_IMPL) {
printf("%sRPRFM", sep);
sep = ",";
}
if (ID_AA64ISAR2_CLRBHB(id) >= ID_AA64ISAR2_CLRBHB_IMPL) {
printf("%sCLRBHB", sep);
sep = ",";
}
if (ID_AA64ISAR2_BC(id) >= ID_AA64ISAR2_BC_IMPL) {
printf("%sBC", sep);
sep = ",";
}
if (ID_AA64ISAR2_MOPS(id) >= ID_AA64ISAR2_MOPS_IMPL) {
printf("%sMOPS", sep);
sep = ",";
}
if (ID_AA64ISAR2_GPA3(id) >= ID_AA64ISAR2_GPA3_IMPL) {
printf("%sGPA3", sep);
sep = ",";
}
if (ID_AA64ISAR2_APA3(id) >= ID_AA64ISAR2_APA3_PAC) {
printf("%sAPA3", sep);
sep = ",";
}
if (ID_AA64ISAR2_APA3(id) == ID_AA64ISAR2_APA3_EPAC)
printf("+EPAC");
else if (ID_AA64ISAR2_APA3(id) >= ID_AA64ISAR2_APA3_EPAC2)
printf("+EPAC2");
if (ID_AA64ISAR2_APA3(id) >= ID_AA64ISAR2_APA3_FPAC)
printf("+FPAC");
if (ID_AA64ISAR2_APA3(id) >= ID_AA64ISAR2_APA3_FPAC_COMBINED)
printf("+COMBINED");
if (ID_AA64ISAR2_RPRES(id) >= ID_AA64ISAR2_RPRES_IMPL) {
printf("%sRPRES", sep);
sep = ",";
}
if (ID_AA64ISAR2_WFXT(id) >= ID_AA64ISAR2_WFXT_IMPL) {
printf("%sWFXT", sep);
sep = ",";
}
id = READ_SPECIALREG(id_aa64mmfr0_el1);
if (ID_AA64MMFR0_ECV(id) >= ID_AA64MMFR0_ECV_IMPL) {
printf("%sECV", sep);
sep = ",";
}
if (ID_AA64MMFR0_ECV(id) >= ID_AA64MMFR0_ECV_CNTHCTL)
printf("+CNTHCTL");
if (ID_AA64MMFR0_ASID_BITS(id) == ID_AA64MMFR0_ASID_BITS_16) {
printf("%sASID16", sep);
sep = ",";
}
id = READ_SPECIALREG(id_aa64mmfr1_el1);
if (ID_AA64MMFR1_AFP(id) >= ID_AA64MMFR1_AFP_IMPL) {
printf("%sAFP", sep);
sep = ",";
}
if (ID_AA64MMFR1_SPECSEI(id) >= ID_AA64MMFR1_SPECSEI_IMPL) {
printf("%sSpecSEI", sep);
sep = ",";
}
if (ID_AA64MMFR1_PAN(id) >= ID_AA64MMFR1_PAN_IMPL) {
printf("%sPAN", sep);
sep = ",";
}
if (ID_AA64MMFR1_PAN(id) >= ID_AA64MMFR1_PAN_ATS1E1)
printf("+ATS1E1");
if (ID_AA64MMFR1_PAN(id) >= ID_AA64MMFR1_PAN_EPAN)
printf("+EPAN");
if (ID_AA64MMFR1_LO(id) >= ID_AA64MMFR1_LO_IMPL) {
printf("%sLO", sep);
sep = ",";
}
if (ID_AA64MMFR1_HPDS(id) >= ID_AA64MMFR1_HPDS_IMPL) {
printf("%sHPDS", sep);
sep = ",";
}
if (ID_AA64MMFR1_VH(id) >= ID_AA64MMFR1_VH_IMPL) {
printf("%sVH", sep);
sep = ",";
}
if (ID_AA64MMFR1_HAFDBS(id) >= ID_AA64MMFR1_HAFDBS_AF) {
printf("%sHAF", sep);
sep = ",";
}
if (ID_AA64MMFR1_HAFDBS(id) >= ID_AA64MMFR1_HAFDBS_AF_DBS)
printf("DBS");
if (ID_AA64MMFR1_ECBHB(id) >= ID_AA64MMFR1_ECBHB_IMPL) {
printf("%sECBHB", sep);
sep = ",";
}
id = READ_SPECIALREG(id_aa64mmfr2_el1);
if (ID_AA64MMFR2_IDS(id) >= ID_AA64MMFR2_IDS_IMPL) {
printf("%sIDS", sep);
sep = ",";
}
if (ID_AA64MMFR2_AT(id) >= ID_AA64MMFR2_AT_IMPL) {
printf("%sAT", sep);
sep = ",";
}
id = READ_SPECIALREG(id_aa64pfr0_el1);
if (ID_AA64PFR0_CSV3(id) >= ID_AA64PFR0_CSV3_IMPL) {
printf("%sCSV3", sep);
sep = ",";
}
if (ID_AA64PFR0_CSV2(id) >= ID_AA64PFR0_CSV2_IMPL) {
printf("%sCSV2", sep);
sep = ",";
}
if (ID_AA64PFR0_CSV2(id) >= ID_AA64PFR0_CSV2_SCXT)
printf("+SCXT");
if (ID_AA64PFR0_CSV2(id) >= ID_AA64PFR0_CSV2_HCXT)
printf("+HCXT");
if (ID_AA64PFR0_DIT(id) >= ID_AA64PFR0_DIT_IMPL) {
printf("%sDIT", sep);
sep = ",";
}
if (ID_AA64PFR0_AMU(id) >= ID_AA64PFR0_AMU_IMPL) {
printf("%sAMU", sep);
if (ID_AA64PFR0_AMU(id) >= ID_AA64PFR0_AMU_IMPL_V1P1)
printf("v1p1");
sep = ",";
}
if (ID_AA64PFR0_RAS(id) >= ID_AA64PFR0_RAS_IMPL) {
printf("%sRAS", sep);
if (ID_AA64PFR0_RAS(id) >= ID_AA64PFR0_RAS_IMPL_V1P1)
printf("v1p1");
sep = ",";
}
if (ID_AA64PFR0_SVE(id) >= ID_AA64PFR0_SVE_IMPL) {
printf("%sSVE", sep);
sep = ",";
}
if (ID_AA64PFR0_ADV_SIMD(id) != ID_AA64PFR0_ADV_SIMD_NONE &&
ID_AA64PFR0_ADV_SIMD(id) >= ID_AA64PFR0_ADV_SIMD_HP) {
printf("%sAdvSIMD+HP", sep);
sep = ",";
}
if (ID_AA64PFR0_FP(id) != ID_AA64PFR0_FP_NONE &&
ID_AA64PFR0_FP(id) >= ID_AA64PFR0_FP_HP) {
printf("%sFP+HP", sep);
sep = ",";
}
id = READ_SPECIALREG(id_aa64pfr1_el1);
if (ID_AA64PFR1_BT(id) >= ID_AA64PFR1_BT_IMPL) {
printf("%sBT", sep);
sep = ",";
}
if (ID_AA64PFR1_SSBS(id) >= ID_AA64PFR1_SSBS_PSTATE) {
printf("%sSSBS", sep);
sep = ",";
}
if (ID_AA64PFR1_SSBS(id) >= ID_AA64PFR1_SSBS_PSTATE_MSR)
printf("+MSR");
if (ID_AA64PFR1_MTE(id) >= ID_AA64PFR1_MTE_IMPL) {
printf("%sMTE", sep);
sep = ",";
}
id = READ_SPECIALREG(id_aa64zfr0_el1);
if (id & ID_AA64ZFR0_MASK) {
printf("\n%s: SVE", ci->ci_dev->dv_xname);
if (ID_AA64ZFR0_SVEVER(id) >= ID_AA64ZFR0_SVEVER_SVE2)
printf("2");
if (ID_AA64ZFR0_SVEVER(id) >= ID_AA64ZFR0_SVEVER_SVE2P1)
printf("p1");
if (ID_AA64ZFR0_F64MM(id) >= ID_AA64ZFR0_F64MM_IMPL)
printf(",F64MM");
if (ID_AA64ZFR0_F32MM(id) >= ID_AA64ZFR0_F32MM_IMPL)
printf(",F32MM");
if (ID_AA64ZFR0_I8MM(id) >= ID_AA64ZFR0_I8MM_IMPL)
printf(",I8MM");
if (ID_AA64ZFR0_SM4(id) >= ID_AA64ZFR0_SM4_IMPL)
printf(",SM4");
if (ID_AA64ZFR0_SHA3(id) >= ID_AA64ZFR0_SHA3_IMPL)
printf(",SHA3");
if (ID_AA64ZFR0_BF16(id) >= ID_AA64ZFR0_BF16_BASE)
printf(",BF16");
if (ID_AA64ZFR0_BF16(id) >= ID_AA64ZFR0_BF16_EBF)
printf("+EBF");
if (ID_AA64ZFR0_BITPERM(id) >= ID_AA64ZFR0_BITPERM_IMPL)
printf(",BitPerm");
if (ID_AA64ZFR0_AES(id) >= ID_AA64ZFR0_AES_BASE)
printf(",AES");
if (ID_AA64ZFR0_AES(id) >= ID_AA64ZFR0_AES_PMULL)
printf("+PMULL");
}
prev_id_aa64isar0 = READ_SPECIALREG(id_aa64isar0_el1);
prev_id_aa64isar1 = READ_SPECIALREG(id_aa64isar1_el1);
prev_id_aa64isar2 = READ_SPECIALREG(id_aa64isar2_el1);
prev_id_aa64mmfr0 = READ_SPECIALREG(id_aa64mmfr0_el1);
prev_id_aa64mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1);
prev_id_aa64mmfr2 = READ_SPECIALREG(id_aa64mmfr2_el1);
prev_id_aa64pfr0 = READ_SPECIALREG(id_aa64pfr0_el1);
prev_id_aa64pfr1 = READ_SPECIALREG(id_aa64pfr1_el1);
prev_id_aa64zfr0 = READ_SPECIALREG(id_aa64zfr0_el1);
#ifdef CPU_DEBUG
id = READ_SPECIALREG(id_aa64afr0_el1);
printf("\nID_AA64AFR0_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64afr1_el1);
printf("\nID_AA64AFR1_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64dfr0_el1);
printf("\nID_AA64DFR0_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64dfr1_el1);
printf("\nID_AA64DFR1_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64isar0_el1);
printf("\nID_AA64ISAR0_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64isar1_el1);
printf("\nID_AA64ISAR1_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64isar2_el1);
printf("\nID_AA64ISAR2_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64mmfr0_el1);
printf("\nID_AA64MMFR0_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64mmfr1_el1);
printf("\nID_AA64MMFR1_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64mmfr2_el1);
printf("\nID_AA64MMFR2_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64pfr0_el1);
printf("\nID_AA64PFR0_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64pfr1_el1);
printf("\nID_AA64PFR1_EL1: 0x%016llx", id);
id = READ_SPECIALREG(id_aa64zfr0_el1);
printf("\nID_AA64ZFR0_EL1: 0x%016llx", id);
#endif
}
void
cpu_identify_cleanup(void)
{
uint64_t id_aa64mmfr2;
uint64_t value;
value = cpu_id_aa64isar0 & ID_AA64ISAR0_MASK;
value &= ~ID_AA64ISAR0_TLB_MASK;
cpu_id_aa64isar0 = value;
value = cpu_id_aa64isar1 & ID_AA64ISAR1_MASK;
value &= ~ID_AA64ISAR1_SPECRES_MASK;
cpu_id_aa64isar1 = value;
value = cpu_id_aa64isar2 & ID_AA64ISAR2_MASK;
value &= ~ID_AA64ISAR2_CLRBHB_MASK;
cpu_id_aa64isar2 = value;
value = 0;
value |= cpu_id_aa64mmfr0 & ID_AA64MMFR0_ECV_MASK;
cpu_id_aa64mmfr0 = value;
value = 0;
value |= cpu_id_aa64mmfr1 & ID_AA64MMFR1_AFP_MASK;
cpu_id_aa64mmfr1 = value;
value = 0;
value |= cpu_id_aa64mmfr2 & ID_AA64MMFR2_AT_MASK;
cpu_id_aa64mmfr2 = value;
value = 0;
value |= cpu_id_aa64pfr0 & ID_AA64PFR0_FP_MASK;
value |= cpu_id_aa64pfr0 & ID_AA64PFR0_ADV_SIMD_MASK;
value |= cpu_id_aa64pfr0 & ID_AA64PFR0_SVE_MASK;
value |= cpu_id_aa64pfr0 & ID_AA64PFR0_DIT_MASK;
cpu_id_aa64pfr0 = value;
value = 0;
value |= cpu_id_aa64pfr1 & ID_AA64PFR1_BT_MASK;
value |= cpu_id_aa64pfr1 & ID_AA64PFR1_SSBS_MASK;
cpu_id_aa64pfr1 = value;
value = cpu_id_aa64zfr0 & ID_AA64ZFR0_MASK;
cpu_id_aa64zfr0 = value;
hwcap |= HWCAP_FP;
hwcap |= HWCAP_ASIMD;
if (ID_AA64ISAR0_AES(cpu_id_aa64isar0) >= ID_AA64ISAR0_AES_BASE)
hwcap |= HWCAP_AES;
if (ID_AA64ISAR0_AES(cpu_id_aa64isar0) >= ID_AA64ISAR0_AES_PMULL)
hwcap |= HWCAP_PMULL;
if (ID_AA64ISAR0_SHA1(cpu_id_aa64isar0) >= ID_AA64ISAR0_SHA1_BASE)
hwcap |= HWCAP_SHA1;
if (ID_AA64ISAR0_SHA2(cpu_id_aa64isar0) >= ID_AA64ISAR0_SHA2_BASE)
hwcap |= HWCAP_SHA2;
if (ID_AA64ISAR0_CRC32(cpu_id_aa64isar0) >= ID_AA64ISAR0_CRC32_BASE)
hwcap |= HWCAP_CRC32;
if (ID_AA64ISAR0_ATOMIC(cpu_id_aa64isar0) >= ID_AA64ISAR0_ATOMIC_IMPL)
hwcap |= HWCAP_ATOMICS;
if (ID_AA64PFR0_FP(cpu_id_aa64pfr0) != ID_AA64PFR0_FP_NONE &&
ID_AA64PFR0_FP(cpu_id_aa64pfr0) >= ID_AA64PFR0_FP_HP)
hwcap |= HWCAP_FPHP;
if (ID_AA64PFR0_ADV_SIMD(cpu_id_aa64pfr0) != ID_AA64PFR0_ADV_SIMD_NONE &&
ID_AA64PFR0_ADV_SIMD(cpu_id_aa64pfr0) >= ID_AA64PFR0_ADV_SIMD_HP)
hwcap |= HWCAP_ASIMDHP;
id_aa64mmfr2 = READ_SPECIALREG(id_aa64mmfr2_el1);
if (ID_AA64MMFR2_IDS(id_aa64mmfr2) >= ID_AA64MMFR2_IDS_IMPL)
hwcap |= HWCAP_CPUID;
if (ID_AA64ISAR0_RDM(cpu_id_aa64isar0) >= ID_AA64ISAR0_RDM_IMPL)
hwcap |= HWCAP_ASIMDRDM;
if (ID_AA64ISAR1_JSCVT(cpu_id_aa64isar1) >= ID_AA64ISAR1_JSCVT_IMPL)
hwcap |= HWCAP_JSCVT;
if (ID_AA64ISAR1_FCMA(cpu_id_aa64isar1) >= ID_AA64ISAR1_FCMA_IMPL)
hwcap |= HWCAP_FCMA;
if (ID_AA64ISAR1_LRCPC(cpu_id_aa64isar1) >= ID_AA64ISAR1_LRCPC_BASE)
hwcap |= HWCAP_LRCPC;
if (ID_AA64ISAR1_DPB(cpu_id_aa64isar1) >= ID_AA64ISAR1_DPB_IMPL)
hwcap |= HWCAP_DCPOP;
if (ID_AA64ISAR0_SHA3(cpu_id_aa64isar0) >= ID_AA64ISAR0_SHA3_IMPL)
hwcap |= HWCAP_SHA3;
if (ID_AA64ISAR0_SM3(cpu_id_aa64isar0) >= ID_AA64ISAR0_SM3_IMPL)
hwcap |= HWCAP_SM3;
if (ID_AA64ISAR0_SM4(cpu_id_aa64isar0) >= ID_AA64ISAR0_SM4_IMPL)
hwcap |= HWCAP_SM4;
if (ID_AA64ISAR0_DP(cpu_id_aa64isar0) >= ID_AA64ISAR0_DP_IMPL)
hwcap |= HWCAP_ASIMDDP;
if (ID_AA64ISAR0_SHA2(cpu_id_aa64isar0) >= ID_AA64ISAR0_SHA2_512)
hwcap |= HWCAP_SHA512;
if (ID_AA64PFR0_SVE(cpu_id_aa64pfr0) >= ID_AA64PFR0_SVE_IMPL)
hwcap |= HWCAP_SVE;
if (ID_AA64ISAR0_FHM(cpu_id_aa64isar0) >= ID_AA64ISAR0_FHM_IMPL)
hwcap |= HWCAP_ASIMDFHM;
if (ID_AA64PFR0_DIT(cpu_id_aa64pfr0) >= ID_AA64PFR0_DIT_IMPL)
hwcap |= HWCAP_DIT;
if (ID_AA64MMFR2_AT(cpu_id_aa64mmfr2) >= ID_AA64MMFR2_AT_IMPL)
hwcap |= HWCAP_USCAT;
if (ID_AA64ISAR1_LRCPC(cpu_id_aa64isar1) >= ID_AA64ISAR1_LRCPC_LDAPUR)
hwcap |= HWCAP_ILRCPC;
if (ID_AA64ISAR0_TS(cpu_id_aa64isar0) >= ID_AA64ISAR0_TS_BASE)
hwcap |= HWCAP_FLAGM;
if (ID_AA64PFR1_SSBS(cpu_id_aa64pfr1) >= ID_AA64PFR1_SSBS_PSTATE_MSR)
hwcap |= HWCAP_SSBS;
if (ID_AA64ISAR1_SB(cpu_id_aa64isar1) >= ID_AA64ISAR1_SB_IMPL)
hwcap |= HWCAP_SB;
if (ID_AA64ISAR1_APA(cpu_id_aa64isar1) >= ID_AA64ISAR1_APA_PAC ||
ID_AA64ISAR1_API(cpu_id_aa64isar1) >= ID_AA64ISAR1_API_PAC ||
ID_AA64ISAR2_APA3(cpu_id_aa64isar2) >= ID_AA64ISAR2_APA3_PAC)
hwcap |= HWCAP_PACA;
if (ID_AA64ISAR1_GPA(cpu_id_aa64isar1) >= ID_AA64ISAR1_GPA_IMPL ||
ID_AA64ISAR1_GPI(cpu_id_aa64isar1) >= ID_AA64ISAR1_GPI_IMPL ||
ID_AA64ISAR2_GPA3(cpu_id_aa64isar2) >= ID_AA64ISAR2_GPA3_IMPL)
hwcap |= HWCAP_PACG;
if (ID_AA64ISAR1_DPB(cpu_id_aa64isar1) >= ID_AA64ISAR1_DPB_DCCVADP)
hwcap2 |= HWCAP2_DCPODP;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_SVEVER(cpu_id_aa64zfr0) >= ID_AA64ZFR0_SVEVER_SVE2)
hwcap2 |= HWCAP2_SVE2;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_AES(cpu_id_aa64zfr0) >= ID_AA64ZFR0_AES_BASE)
hwcap2 |= HWCAP2_SVEAES;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_AES(cpu_id_aa64zfr0) >= ID_AA64ZFR0_AES_PMULL)
hwcap2 |= HWCAP2_SVEPMULL;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_BITPERM(cpu_id_aa64zfr0) >= ID_AA64ZFR0_BITPERM_IMPL)
hwcap2 |= HWCAP2_SVEBITPERM;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_SHA3(cpu_id_aa64zfr0) >= ID_AA64ZFR0_SHA3_IMPL)
hwcap2 |= HWCAP2_SVESHA3;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_SM4(cpu_id_aa64zfr0) >= ID_AA64ZFR0_SM4_IMPL)
hwcap2 |= HWCAP2_SVESM4;
if (ID_AA64ISAR0_TS(cpu_id_aa64isar0) >= ID_AA64ISAR0_TS_AXFLAG)
hwcap2 |= HWCAP2_FLAGM2;
if (ID_AA64ISAR1_FRINTTS(cpu_id_aa64isar1) >= ID_AA64ISAR1_FRINTTS_IMPL)
hwcap2 |= HWCAP2_FRINT;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_I8MM(cpu_id_aa64zfr0) >= ID_AA64ZFR0_I8MM_IMPL)
hwcap2 |= HWCAP2_SVEI8MM;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_F32MM(cpu_id_aa64zfr0) >= ID_AA64ZFR0_F32MM_IMPL)
hwcap2 |= HWCAP2_SVEF32MM;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_F64MM(cpu_id_aa64zfr0) >= ID_AA64ZFR0_F64MM_IMPL)
hwcap2 |= HWCAP2_SVEF64MM;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_BF16(cpu_id_aa64zfr0) >= ID_AA64ZFR0_BF16_BASE)
hwcap2 |= HWCAP2_SVEBF16;
if (ID_AA64ISAR1_I8MM(cpu_id_aa64isar1) >= ID_AA64ISAR1_I8MM_IMPL)
hwcap2 |= HWCAP2_I8MM;
if (ID_AA64ISAR1_BF16(cpu_id_aa64isar1) >= ID_AA64ISAR1_BF16_BASE)
hwcap2 |= HWCAP2_BF16;
if (ID_AA64ISAR1_DGH(cpu_id_aa64isar1) >= ID_AA64ISAR1_DGH_IMPL)
hwcap2 |= HWCAP2_DGH;
if (ID_AA64ISAR0_RNDR(cpu_id_aa64isar0) >= ID_AA64ISAR0_RNDR_IMPL)
hwcap2 |= HWCAP2_RNG;
if (ID_AA64PFR1_BT(cpu_id_aa64pfr1) >= ID_AA64PFR1_BT_IMPL)
hwcap2 |= HWCAP2_BTI;
if (ID_AA64MMFR0_ECV(cpu_id_aa64mmfr0) >= ID_AA64MMFR0_ECV_IMPL)
hwcap2 |= HWCAP2_ECV;
if (ID_AA64MMFR1_AFP(cpu_id_aa64mmfr1) >= ID_AA64MMFR1_AFP_IMPL)
hwcap2 |= HWCAP2_AFP;
if (ID_AA64ISAR2_RPRES(cpu_id_aa64isar2) >= ID_AA64ISAR2_RPRES_IMPL)
hwcap2 |= HWCAP2_RPRES;
if (ID_AA64ISAR2_WFXT(cpu_id_aa64isar2) >= ID_AA64ISAR2_WFXT_IMPL)
hwcap2 |= HWCAP2_WFXT;
if (ID_AA64ISAR1_BF16(cpu_id_aa64isar1) >= ID_AA64ISAR1_BF16_EBF)
hwcap2 |= HWCAP2_EBF16;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_BF16(cpu_id_aa64zfr0) >= ID_AA64ZFR0_BF16_EBF)
hwcap2 |= HWCAP2_SVE_EBF16;
if (ID_AA64ISAR2_CSSC(cpu_id_aa64isar2) >= ID_AA64ISAR2_CSSC_IMPL)
hwcap2 |= HWCAP2_CSSC;
if (ID_AA64ISAR2_RPRFM(cpu_id_aa64isar2) >= ID_AA64ISAR2_RPRFM_IMPL)
hwcap2 |= HWCAP2_RPRFM;
if ((hwcap & HWCAP_SVE) &&
ID_AA64ZFR0_SVEVER(cpu_id_aa64zfr0) >= ID_AA64ZFR0_SVEVER_SVE2P1)
hwcap2 |= HWCAP2_SVE2P1;
if (ID_AA64ISAR2_MOPS(cpu_id_aa64isar2) >= ID_AA64ISAR2_MOPS_IMPL)
hwcap2 |= HWCAP2_MOPS;
if (ID_AA64ISAR2_BC(cpu_id_aa64isar2) >= ID_AA64ISAR2_BC_IMPL)
hwcap2 |= HWCAP2_HBC;
}
void
cpu_classify(void)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
uint64_t max_capacity = 0;
CPU_INFO_FOREACH(cii, ci) {
max_capacity = MAX(max_capacity, ci->ci_capacity);
}
CPU_INFO_FOREACH(cii, ci) {
if (ci->ci_capacity == 0)
ci->ci_cputype = CPUTYP_P;
else if (100 * ci->ci_capacity > 80 * max_capacity)
ci->ci_cputype = CPUTYP_P;
else if (100 * ci->ci_capacity > 30 * max_capacity)
ci->ci_cputype = CPUTYP_E;
else
ci->ci_cputype = CPUTYP_L;
}
}
void cpu_init(void);
int cpu_start_secondary(struct cpu_info *ci, int, uint64_t);
int cpu_clockspeed(int *);
int
cpu_match(struct device *parent, void *cfdata, void *aux)
{
struct fdt_attach_args *faa = aux;
uint64_t mpidr = READ_SPECIALREG(mpidr_el1);
char buf[32];
if (OF_getprop(faa->fa_node, "device_type", buf, sizeof(buf)) <= 0 ||
strcmp(buf, "cpu") != 0)
return 0;
if (ncpus < MAXCPUS || faa->fa_reg[0].addr == (mpidr & MPIDR_AFF))
return 1;
return 0;
}
void
cpu_attach(struct device *parent, struct device *dev, void *aux)
{
struct fdt_attach_args *faa = aux;
struct cpu_info *ci;
void *kstack;
#ifdef MULTIPROCESSOR
struct cpu_info *ci_last;
uint64_t mpidr = READ_SPECIALREG(mpidr_el1);
#endif
uint32_t opp;
KASSERT(faa->fa_nreg > 0);
#ifdef MULTIPROCESSOR
if (faa->fa_reg[0].addr == (mpidr & MPIDR_AFF)) {
ci = &cpu_info_primary;
ci->ci_flags |= CPUF_RUNNING | CPUF_PRESENT | CPUF_PRIMARY;
} else {
ci = malloc(sizeof(*ci), M_DEVBUF, M_WAITOK | M_ZERO);
cpu_info[dev->dv_unit] = ci;
ci_last = cpu_info_list;
while (ci_last->ci_next != NULL)
ci_last = ci_last->ci_next;
ci_last->ci_next = ci;
ci->ci_flags |= CPUF_AP;
ncpus++;
}
#else
ci = &cpu_info_primary;
#endif
ci->ci_dev = dev;
ci->ci_cpuid = dev->dv_unit;
ci->ci_mpidr = faa->fa_reg[0].addr;
ci->ci_node = faa->fa_node;
ci->ci_self = ci;
printf(" mpidr %llx:", ci->ci_mpidr);
kstack = km_alloc(USPACE, &kv_any, &kp_zero, &kd_waitok);
ci->ci_el1_stkend = (vaddr_t)kstack + USPACE - 16;
ci->ci_trampoline_vectors = (vaddr_t)trampoline_vectors_none;
#ifdef MULTIPROCESSOR
if (ci->ci_flags & CPUF_AP) {
char buf[32];
uint64_t spinup_data = 0;
int spinup_method = 0;
int timeout = 10000;
int len;
len = OF_getprop(ci->ci_node, "enable-method",
buf, sizeof(buf));
if (strcmp(buf, "psci") == 0) {
spinup_method = 1;
} else if (strcmp(buf, "spin-table") == 0) {
spinup_method = 2;
spinup_data = OF_getpropint64(ci->ci_node,
"cpu-release-addr", 0);
}
clockqueue_init(&ci->ci_queue);
sched_init_cpu(ci);
if (cpu_start_secondary(ci, spinup_method, spinup_data)) {
atomic_setbits_int(&ci->ci_flags, CPUF_IDENTIFY);
__asm volatile("dsb sy; sev" ::: "memory");
while ((ci->ci_flags & CPUF_IDENTIFIED) == 0 &&
--timeout)
delay(1000);
if (timeout == 0) {
printf(" failed to identify");
ci->ci_flags = 0;
}
} else {
printf(" failed to spin up");
ci->ci_flags = 0;
}
} else {
#endif
cpu_id_aa64isar0 = READ_SPECIALREG(id_aa64isar0_el1);
cpu_id_aa64isar1 = READ_SPECIALREG(id_aa64isar1_el1);
cpu_id_aa64isar2 = READ_SPECIALREG(id_aa64isar2_el1);
cpu_id_aa64mmfr0 = READ_SPECIALREG(id_aa64mmfr0_el1);
cpu_id_aa64mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1);
cpu_id_aa64mmfr2 = READ_SPECIALREG(id_aa64mmfr2_el1);
cpu_id_aa64pfr0 = READ_SPECIALREG(id_aa64pfr0_el1);
cpu_id_aa64pfr1 = READ_SPECIALREG(id_aa64pfr1_el1);
cpu_id_aa64zfr0 = READ_SPECIALREG(id_aa64zfr0_el1);
cpu_id_aa64mmfr1 &= ~ID_AA64MMFR1_SPECSEI_MASK;
cpu_id_aa64pfr0 &= ~ID_AA64PFR0_CSV2_MASK;
cpu_id_aa64pfr0 &= ~ID_AA64PFR0_CSV3_MASK;
cpu_id_aa64pfr0 &= ~ID_AA64PFR0_EL0_MASK;
cpu_id_aa64pfr0 &= ~ID_AA64PFR0_EL1_MASK;
cpu_id_aa64pfr0 &= ~ID_AA64PFR0_EL2_MASK;
cpu_id_aa64pfr0 &= ~ID_AA64PFR0_EL3_MASK;
if (hw_vendor && hw_prod && strcmp(hw_vendor, "LENOVO") == 0) {
if (strncmp(hw_prod, "21BX", 4) == 0 ||
strncmp(hw_prod, "21BY", 4) == 0) {
cpu_id_aa64isar1 &= ~ID_AA64ISAR1_APA_MASK;
cpu_id_aa64isar1 &= ~ID_AA64ISAR1_GPA_MASK;
}
}
cpu_identify(ci);
if (OF_getproplen(ci->ci_node, "clocks") > 0) {
cpu_node = ci->ci_node;
cpu_cpuspeed = cpu_clockspeed;
}
cpu_init();
if (arm64_has_rng) {
timeout_set(&cpu_rng_to, cpu_rng, &cpu_rng_to);
cpu_rng(&cpu_rng_to);
}
#ifdef MULTIPROCESSOR
}
#if NXCALL > 0
cpu_xcall_establish(ci);
#endif
#endif
#if NKSTAT > 0
cpu_kstat_attach(ci);
#endif
ci->ci_capacity = OF_getpropint(ci->ci_node, "capacity-dmips-mhz", 0);
opp = OF_getpropint(ci->ci_node, "operating-points-v2", 0);
if (opp) {
cpu_opp_init(ci, opp);
ci->ci_capacity *= ci->ci_opp_table->ot_opp_hz_max / 1000000;
}
cpu_classify();
cpu_psci_init(ci);
printf("\n");
}
void
cpu_init(void)
{
uint64_t id_aa64mmfr1, sctlr;
uint64_t id_aa64pfr0;
uint64_t cpacr;
uint64_t tcr;
WRITE_SPECIALREG(ttbr0_el1, pmap_kernel()->pm_pt0pa);
__asm volatile("isb");
tcr = READ_SPECIALREG(tcr_el1);
tcr &= ~TCR_T0SZ(0x3f);
tcr |= TCR_T0SZ(64 - USER_SPACE_BITS);
tcr |= TCR_A1;
WRITE_SPECIALREG(tcr_el1, tcr);
cpu_tlb_flush();
id_aa64mmfr1 = READ_SPECIALREG(id_aa64mmfr1_el1);
if (ID_AA64MMFR1_PAN(id_aa64mmfr1) >= ID_AA64MMFR1_PAN_IMPL) {
sctlr = READ_SPECIALREG(sctlr_el1);
sctlr &= ~SCTLR_SPAN;
if (ID_AA64MMFR1_PAN(id_aa64mmfr1) >= ID_AA64MMFR1_PAN_EPAN)
sctlr |= SCTLR_EPAN;
WRITE_SPECIALREG(sctlr_el1, sctlr);
}
id_aa64pfr0 = READ_SPECIALREG(id_aa64pfr0_el1);
if (ID_AA64PFR0_DIT(id_aa64pfr0) >= ID_AA64PFR0_DIT_IMPL)
__asm volatile (".arch armv8.4-a; msr dit, #1");
if (ID_AA64ISAR1_APA(cpu_id_aa64isar1) >= ID_AA64ISAR1_APA_PAC ||
ID_AA64ISAR1_API(cpu_id_aa64isar1) >= ID_AA64ISAR1_API_PAC ||
ID_AA64ISAR2_APA3(cpu_id_aa64isar2) >= ID_AA64ISAR2_APA3_PAC) {
sctlr = READ_SPECIALREG(sctlr_el1);
sctlr |= SCTLR_EnIA | SCTLR_EnDA;
sctlr |= SCTLR_EnIB | SCTLR_EnDB;
WRITE_SPECIALREG(sctlr_el1, sctlr);
}
if (ID_AA64PFR1_BT(cpu_id_aa64pfr1) >= ID_AA64PFR1_BT_IMPL) {
sctlr = READ_SPECIALREG(sctlr_el1);
sctlr |= SCTLR_BT0 | SCTLR_BT1;
WRITE_SPECIALREG(sctlr_el1, sctlr);
}
if (ID_AA64PFR0_SVE(cpu_id_aa64pfr0) >= ID_AA64PFR0_SVE_IMPL) {
cpacr = READ_SPECIALREG(cpacr_el1);
cpacr &= ~CPACR_ZEN_MASK;
cpacr |= CPACR_ZEN_TRAP_EL0;
WRITE_SPECIALREG(cpacr_el1, cpacr);
__asm volatile ("isb");
WRITE_SPECIALREG(zcr_el1, 0);
cpacr &= ~CPACR_ZEN_MASK;
cpacr |= CPACR_ZEN_TRAP_ALL1;
WRITE_SPECIALREG(cpacr_el1, cpacr);
__asm volatile ("isb");
}
WRITE_SPECIALREG(mdscr_el1, DBG_MDSCR_TDCC);
WRITE_SPECIALREG(oslar_el1, 0);
}
void
cpu_flush_bp_noop(void)
{
}
void
cpu_flush_bp_psci(void)
{
#if NPSCI > 0
psci_flush_bp();
#endif
}
void
cpu_serror_apple(void)
{
__asm volatile("dsb sy; isb" ::: "memory");
printf("l2c_err_sts 0x%llx\n", READ_SPECIALREG(s3_3_c15_c8_0));
printf("l2c_err_adr 0x%llx\n", READ_SPECIALREG(s3_3_c15_c9_0));
printf("l2c_err_inf 0x%llx\n", READ_SPECIALREG(s3_3_c15_c10_0));
}
int
cpu_clockspeed(int *freq)
{
*freq = clock_get_frequency(cpu_node, NULL) / 1000000;
return 0;
}
#ifdef MULTIPROCESSOR
void cpu_boot_secondary(struct cpu_info *ci);
void cpu_hatch_secondary(void);
void cpu_hatch_secondary_spin(void);
void cpu_suspend_cycle(void);
void
cpu_boot_secondary_processors(void)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
CPU_INFO_FOREACH(cii, ci) {
if ((ci->ci_flags & CPUF_AP) == 0)
continue;
if (ci->ci_flags & CPUF_PRIMARY)
continue;
ci->ci_randseed = (arc4random() & 0x7fffffff) + 1;
cpu_boot_secondary(ci);
}
}
void
cpu_start_spin_table(struct cpu_info *ci, uint64_t start, uint64_t data)
{
extern paddr_t cpu_hatch_ci;
pmap_extract(pmap_kernel(), (vaddr_t)ci, &cpu_hatch_ci);
cpu_dcache_wb_range((vaddr_t)&cpu_hatch_ci, sizeof(paddr_t));
vaddr_t start_pg = zero_page + (PAGE_SIZE * ci->ci_cpuid);
paddr_t pa = trunc_page(data);
uint64_t offset = data - pa;
uint64_t *startvec = (uint64_t *)(start_pg + offset);
pmap_kenter_cache(start_pg, pa, PROT_READ|PROT_WRITE, PMAP_CACHE_CI);
*startvec = start;
__asm volatile("dsb sy; sev" ::: "memory");
pmap_kremove(start_pg, PAGE_SIZE);
}
int
cpu_start_secondary(struct cpu_info *ci, int method, uint64_t data)
{
vaddr_t start_va;
paddr_t ci_pa, start_pa;
uint64_t ttbr1;
int32_t status;
__asm("mrs %x0, ttbr1_el1": "=r"(ttbr1));
ci->ci_ttbr1 = ttbr1;
cpu_dcache_wb_range((vaddr_t)ci, sizeof(*ci));
switch (method) {
#if NPSCI > 0
case 1:
start_va = (vaddr_t)cpu_hatch_secondary;
pmap_extract(pmap_kernel(), start_va, &start_pa);
pmap_extract(pmap_kernel(), (vaddr_t)ci, &ci_pa);
status = psci_cpu_on(ci->ci_mpidr, start_pa, ci_pa);
return (status == PSCI_SUCCESS);
#endif
case 2:
start_va = (vaddr_t)cpu_hatch_secondary_spin;
pmap_extract(pmap_kernel(), start_va, &start_pa);
cpu_start_spin_table(ci, start_pa, data);
return 1;
}
return 0;
}
void
cpu_boot_secondary(struct cpu_info *ci)
{
atomic_setbits_int(&ci->ci_flags, CPUF_GO);
__asm volatile("dsb sy; sev" ::: "memory");
arm_send_ipi(ci, ARM_IPI_NOP);
while ((ci->ci_flags & CPUF_RUNNING) == 0)
__asm volatile("wfe");
}
void
cpu_init_secondary(struct cpu_info *ci)
{
struct proc *p;
struct pcb *pcb;
struct trapframe *tf;
struct switchframe *sf;
int s;
ci->ci_flags |= CPUF_PRESENT;
__asm volatile("dsb sy" ::: "memory");
if ((ci->ci_flags & CPUF_IDENTIFIED) == 0) {
while ((ci->ci_flags & CPUF_IDENTIFY) == 0)
__asm volatile("wfe");
cpu_identify(ci);
atomic_setbits_int(&ci->ci_flags, CPUF_IDENTIFIED);
__asm volatile("dsb sy" ::: "memory");
}
while ((ci->ci_flags & CPUF_GO) == 0)
__asm volatile("wfe");
cpu_init();
ci->ci_curproc = NULL;
ci->ci_curpcb = NULL;
ci->ci_curpm = NULL;
ci->ci_cpl = IPL_NONE;
ci->ci_ipending = 0;
ci->ci_idepth = 0;
#ifdef DIAGNOSTIC
ci->ci_mutex_level = 0;
#endif
p = ci->ci_schedstate.spc_idleproc;
pcb = &p->p_addr->u_pcb;
tf = (struct trapframe *)((u_long)p->p_addr
+ USPACE
- sizeof(struct trapframe)
- 0x10);
tf = (struct trapframe *)STACKALIGN(tf);
pcb->pcb_tf = tf;
sf = (struct switchframe *)tf - 1;
sf->sf_x19 = (uint64_t)sched_idle;
sf->sf_x20 = (uint64_t)ci;
sf->sf_lr = (uint64_t)proc_trampoline;
pcb->pcb_sp = (uint64_t)sf;
s = splhigh();
arm_intr_cpu_enable();
cpu_startclock();
atomic_setbits_int(&ci->ci_flags, CPUF_RUNNING);
__asm volatile("dsb sy; sev" ::: "memory");
spllower(IPL_NONE);
sched_toidle();
}
void
cpu_halt(void)
{
struct cpu_info *ci = curcpu();
vaddr_t start_va;
paddr_t ci_pa, start_pa;
int count = 0;
u_long psw;
int32_t status;
KERNEL_ASSERT_UNLOCKED();
SCHED_ASSERT_UNLOCKED();
start_va = (vaddr_t)cpu_hatch_secondary;
pmap_extract(pmap_kernel(), start_va, &start_pa);
pmap_extract(pmap_kernel(), (vaddr_t)ci, &ci_pa);
psw = intr_disable();
atomic_clearbits_int(&ci->ci_flags,
CPUF_RUNNING | CPUF_PRESENT | CPUF_GO);
#if NPSCI > 0
if (psci_can_suspend())
psci_cpu_off();
#endif
atomic_setbits_int(&ci->ci_flags, CPUF_PRESENT);
WRITE_SPECIALREG(cntv_ctl_el0,
READ_SPECIALREG(cntv_ctl_el0) | CNTV_CTL_IMASK);
while ((ci->ci_flags & CPUF_GO) == 0) {
#if NPSCI > 0
if (ci->ci_psci_suspend_param) {
status = psci_cpu_suspend(ci->ci_psci_suspend_param,
start_pa, ci_pa);
if (status != PSCI_SUCCESS)
ci->ci_psci_suspend_param = 0;
} else
#endif
cpu_suspend_cycle();
count++;
}
atomic_setbits_int(&ci->ci_flags, CPUF_RUNNING);
__asm volatile("dsb sy; sev" ::: "memory");
intr_restore(psw);
WRITE_SPECIALREG(cntv_ctl_el0,
READ_SPECIALREG(cntv_ctl_el0) & ~CNTV_CTL_IMASK);
}
void
cpu_kick(struct cpu_info *ci)
{
if (ci != curcpu())
arm_send_ipi(ci, ARM_IPI_NOP);
}
void
cpu_unidle(struct cpu_info *ci)
{
if (ci != curcpu())
arm_send_ipi(ci, ARM_IPI_NOP);
}
#endif
int cpu_suspended;
#ifdef SUSPEND
void cpu_hatch_primary(void);
void (*cpu_suspend_cycle_fcn)(void) = cpu_wfi;
label_t cpu_suspend_jmpbuf;
void
cpu_suspend_cycle(void)
{
cpu_suspend_cycle_fcn();
}
void
cpu_init_primary(void)
{
cpu_init();
cpu_startclock();
longjmp(&cpu_suspend_jmpbuf);
}
int
cpu_suspend_primary(void)
{
struct cpu_info *ci = curcpu();
vaddr_t start_va;
paddr_t ci_pa, start_pa;
uint64_t ttbr1;
int32_t status;
int count = 0;
__asm("mrs %x0, ttbr1_el1": "=r"(ttbr1));
ci->ci_ttbr1 = ttbr1;
cpu_dcache_wb_range((vaddr_t)ci, sizeof(*ci));
start_va = (vaddr_t)cpu_hatch_primary;
pmap_extract(pmap_kernel(), start_va, &start_pa);
pmap_extract(pmap_kernel(), (vaddr_t)ci, &ci_pa);
#if NPSCI > 0
if (psci_can_suspend()) {
if (setjmp(&cpu_suspend_jmpbuf)) {
delay(200000);
return 0;
}
psci_system_suspend(start_pa, ci_pa);
return EOPNOTSUPP;
}
#endif
if (setjmp(&cpu_suspend_jmpbuf))
goto resume;
WRITE_SPECIALREG(cntv_ctl_el0,
READ_SPECIALREG(cntv_ctl_el0) | CNTV_CTL_IMASK);
cpu_suspended = 1;
arm_intr_func.setipl(IPL_NONE);
intr_enable();
while (cpu_suspended) {
#if NPSCI > 0
if (ci->ci_psci_suspend_param) {
status = psci_cpu_suspend(ci->ci_psci_suspend_param,
start_pa, ci_pa);
if (status != PSCI_SUCCESS)
ci->ci_psci_suspend_param = 0;
} else
#endif
cpu_suspend_cycle();
count++;
}
resume:
intr_disable();
arm_intr_func.setipl(IPL_HIGH);
WRITE_SPECIALREG(cntv_ctl_el0,
READ_SPECIALREG(cntv_ctl_el0) & ~CNTV_CTL_IMASK);
return 0;
}
#ifdef MULTIPROCESSOR
void
cpu_resume_secondary(struct cpu_info *ci)
{
int timeout = 10000;
if (ci->ci_flags & CPUF_PRESENT)
return;
cpu_start_secondary(ci, 1, 0);
while ((ci->ci_flags & CPUF_PRESENT) == 0 && --timeout)
delay(1000);
if (timeout == 0) {
printf("%s: failed to spin up\n",
ci->ci_dev->dv_xname);
ci->ci_flags = 0;
}
}
#endif
#endif
extern int perflevel;
LIST_HEAD(, opp_table) opp_tables = LIST_HEAD_INITIALIZER(opp_tables);
struct task cpu_opp_task;
void cpu_opp_mountroot(struct device *);
void cpu_opp_dotask(void *);
void cpu_opp_setperf(int);
uint32_t cpu_opp_get_cooling_level(void *, uint32_t *);
void cpu_opp_set_cooling_level(void *, uint32_t *, uint32_t);
void
cpu_opp_init(struct cpu_info *ci, uint32_t phandle)
{
struct opp_table *ot;
struct cooling_device *cd;
int count, node, child;
uint32_t opp_hz, opp_microvolt;
uint32_t values[3];
int i, j, len;
LIST_FOREACH(ot, &opp_tables, ot_list) {
if (ot->ot_phandle == phandle) {
ci->ci_opp_table = ot;
return;
}
}
node = OF_getnodebyphandle(phandle);
if (node == 0)
return;
if (!OF_is_compatible(node, "operating-points-v2"))
return;
count = 0;
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
if (OF_getproplen(child, "turbo-mode") == 0)
continue;
count++;
}
if (count == 0)
return;
ot = malloc(sizeof(struct opp_table), M_DEVBUF, M_ZERO | M_WAITOK);
ot->ot_phandle = phandle;
ot->ot_opp = mallocarray(count, sizeof(struct opp),
M_DEVBUF, M_ZERO | M_WAITOK);
ot->ot_nopp = count;
count = 0;
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
if (OF_getproplen(child, "turbo-mode") == 0)
continue;
opp_hz = OF_getpropint64(child, "opp-hz", 0);
len = OF_getpropintarray(child, "opp-microvolt",
values, sizeof(values));
opp_microvolt = 0;
if (len == sizeof(uint32_t) || len == 3 * sizeof(uint32_t))
opp_microvolt = values[0];
for (i = 0; i < count; i++) {
if (opp_hz < ot->ot_opp[i].opp_hz)
break;
}
for (j = count; j > i; j--)
ot->ot_opp[j] = ot->ot_opp[j - 1];
ot->ot_opp[i].opp_hz = opp_hz;
ot->ot_opp[i].opp_microvolt = opp_microvolt;
count++;
}
ot->ot_opp_hz_min = ot->ot_opp[0].opp_hz;
ot->ot_opp_hz_max = ot->ot_opp[count - 1].opp_hz;
if (OF_getproplen(node, "opp-shared") == 0)
ot->ot_master = ci;
LIST_INSERT_HEAD(&opp_tables, ot, ot_list);
ci->ci_opp_table = ot;
ci->ci_opp_max = ot->ot_nopp - 1;
ci->ci_cpu_supply = OF_getpropint(ci->ci_node, "cpu-supply", 0);
cd = malloc(sizeof(struct cooling_device), M_DEVBUF, M_ZERO | M_WAITOK);
cd->cd_node = ci->ci_node;
cd->cd_cookie = ci;
cd->cd_get_level = cpu_opp_get_cooling_level;
cd->cd_set_level = cpu_opp_set_cooling_level;
cooling_device_register(cd);
config_mountroot(ci->ci_dev, cpu_opp_mountroot);
}
void
cpu_opp_mountroot(struct device *self)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
int count = 0;
int level = 0;
if (cpu_setperf)
return;
CPU_INFO_FOREACH(cii, ci) {
struct opp_table *ot = ci->ci_opp_table;
uint64_t curr_hz;
uint32_t curr_microvolt;
int error;
if (ot == NULL)
continue;
#if NKSTAT > 0
cpu_opp_kstat_attach(ci);
#endif
if (ot->ot_master && ot->ot_master != ci)
continue;
regulator_enable(ci->ci_cpu_supply);
curr_hz = clock_get_frequency(ci->ci_node, NULL);
curr_microvolt = regulator_get_voltage(ci->ci_cpu_supply);
error = ENODEV;
if (curr_hz != 0)
error = clock_set_frequency(ci->ci_node, NULL, curr_hz);
if (error) {
ci->ci_opp_table = NULL;
printf("%s: clock not implemented\n",
ci->ci_dev->dv_xname);
continue;
}
error = ci->ci_cpu_supply ? ENODEV : 0;
if (ci->ci_cpu_supply && curr_microvolt != 0)
error = regulator_set_voltage(ci->ci_cpu_supply,
curr_microvolt);
if (error) {
ci->ci_opp_table = NULL;
printf("%s: regulator not implemented\n",
ci->ci_dev->dv_xname);
continue;
}
if (level == 0) {
uint64_t min, max;
uint64_t level_hz;
min = ot->ot_opp_hz_min;
max = ot->ot_opp_hz_max;
level_hz = clock_get_frequency(ci->ci_node, NULL);
if (level_hz < min)
level_hz = min;
if (level_hz > max)
level_hz = max;
level = howmany(100 * (level_hz - min), (max - min));
}
count++;
}
if (count > 0) {
task_set(&cpu_opp_task, cpu_opp_dotask, NULL);
cpu_setperf = cpu_opp_setperf;
perflevel = (level > 0) ? level : 0;
cpu_setperf(perflevel);
}
}
void
cpu_opp_dotask(void *arg)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
CPU_INFO_FOREACH(cii, ci) {
struct opp_table *ot = ci->ci_opp_table;
uint64_t curr_hz, opp_hz;
uint32_t curr_microvolt, opp_microvolt;
int opp_idx;
int error = 0;
if (ot == NULL)
continue;
if (ot->ot_master && ot->ot_master != ci)
continue;
opp_idx = MIN(ci->ci_opp_idx, ci->ci_opp_max);
opp_hz = ot->ot_opp[opp_idx].opp_hz;
opp_microvolt = ot->ot_opp[opp_idx].opp_microvolt;
curr_hz = clock_get_frequency(ci->ci_node, NULL);
curr_microvolt = regulator_get_voltage(ci->ci_cpu_supply);
if (error == 0 && opp_hz < curr_hz)
error = clock_set_frequency(ci->ci_node, NULL, opp_hz);
if (error == 0 && ci->ci_cpu_supply &&
opp_microvolt != 0 && opp_microvolt != curr_microvolt) {
error = regulator_set_voltage(ci->ci_cpu_supply,
opp_microvolt);
}
if (error == 0 && opp_hz > curr_hz)
error = clock_set_frequency(ci->ci_node, NULL, opp_hz);
if (error)
printf("%s: DVFS failed\n", ci->ci_dev->dv_xname);
}
}
void
cpu_opp_setperf(int level)
{
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
CPU_INFO_FOREACH(cii, ci) {
struct opp_table *ot = ci->ci_opp_table;
uint64_t min, max;
uint64_t level_hz, opp_hz;
int opp_idx = -1;
int i;
if (ot == NULL)
continue;
if (ot->ot_master && ot->ot_master != ci)
continue;
min = ot->ot_opp_hz_min;
max = ot->ot_opp_hz_max;
level_hz = min + (level * (max - min)) / 100;
opp_hz = min;
for (i = 0; i < ot->ot_nopp; i++) {
if (ot->ot_opp[i].opp_hz <= level_hz &&
ot->ot_opp[i].opp_hz >= opp_hz)
opp_hz = ot->ot_opp[i].opp_hz;
}
for (i = 0; i < ot->ot_nopp; i++) {
if (ot->ot_opp[i].opp_hz == opp_hz) {
opp_idx = i;
break;
}
}
KASSERT(opp_idx >= 0);
ci->ci_opp_idx = opp_idx;
}
task_add(systq, &cpu_opp_task);
}
uint32_t
cpu_opp_get_cooling_level(void *cookie, uint32_t *cells)
{
struct cpu_info *ci = cookie;
struct opp_table *ot = ci->ci_opp_table;
return ot->ot_nopp - ci->ci_opp_max - 1;
}
void
cpu_opp_set_cooling_level(void *cookie, uint32_t *cells, uint32_t level)
{
struct cpu_info *ci = cookie;
struct opp_table *ot = ci->ci_opp_table;
int opp_max;
if (level > (ot->ot_nopp - 1))
level = ot->ot_nopp - 1;
opp_max = (ot->ot_nopp - level - 1);
if (ci->ci_opp_max != opp_max) {
ci->ci_opp_max = opp_max;
task_add(systq, &cpu_opp_task);
}
}
void
cpu_psci_init(struct cpu_info *ci)
{
uint32_t *domains;
uint32_t *domain;
uint32_t *states;
uint32_t ncells;
uint32_t cluster;
int idx, len, node;
len = OF_getproplen(ci->ci_node, "cpu-idle-states");
if (len < (int)sizeof(uint32_t))
return;
states = malloc(len, M_TEMP, M_WAITOK);
OF_getpropintarray(ci->ci_node, "cpu-idle-states", states, len);
node = OF_getnodebyphandle(states[0]);
free(states, M_TEMP, len);
if (node) {
uint32_t entry, exit, residency, param;
int32_t features;
param = OF_getpropint(node, "arm,psci-suspend-param", 0);
entry = OF_getpropint(node, "entry-latency-us", 0);
exit = OF_getpropint(node, "exit-latency-us", 0);
residency = OF_getpropint(node, "min-residency-us", 0);
ci->ci_psci_idle_latency += entry + exit + 2 * residency;
if (OF_getpropbool(node, "local-timer-stop"))
ci->ci_psci_idle_param = 0;
features = psci_features(CPU_SUSPEND);
if (features == PSCI_NOT_SUPPORTED ||
(features & PSCI_FEATURE_POWER_STATE_EXT) == 0) {
if (param & PSCI_POWER_STATE_POWERDOWN)
param = 0;
} else {
if (param & PSCI_POWER_STATE_EXT_POWERDOWN)
param = 0;
}
if (param) {
ci->ci_psci_idle_param = param;
cpu_idle_cycle_fcn = cpu_psci_idle_cycle;
}
}
idx = OF_getindex(ci->ci_node, "psci", "power-domain-names");
if (idx < 0)
return;
len = OF_getproplen(ci->ci_node, "power-domains");
if (len <= 0)
return;
domains = malloc(len, M_TEMP, M_WAITOK);
OF_getpropintarray(ci->ci_node, "power-domains", domains, len);
domain = domains;
while (domain && domain < domains + (len / sizeof(uint32_t))) {
if (idx == 0)
break;
node = OF_getnodebyphandle(domain[0]);
if (node == 0)
break;
ncells = OF_getpropint(node, "#power-domain-cells", 0);
domain = domain + ncells + 1;
idx--;
}
node = idx == 0 ? OF_getnodebyphandle(domain[0]) : 0;
free(domains, M_TEMP, len);
if (node == 0)
return;
cluster = OF_getpropint(node, "power-domains", 0);
len = OF_getproplen(node, "domain-idle-states");
if (len < (int)sizeof(uint32_t))
return;
states = malloc(len, M_TEMP, M_WAITOK);
OF_getpropintarray(node, "domain-idle-states", states, len);
node = OF_getnodebyphandle(states[len / sizeof(uint32_t) - 1]);
free(states, M_TEMP, len);
if (node == 0)
return;
ci->ci_psci_suspend_param =
OF_getpropint(node, "arm,psci-suspend-param", 0);
#ifdef MULTIPROCESSOR
if (ci->ci_flags & CPUF_AP)
return;
#endif
node = OF_getnodebyphandle(cluster);
if (node == 0)
return;
states = malloc(len, M_TEMP, M_WAITOK);
OF_getpropintarray(node, "domain-idle-states", states, len);
node = OF_getnodebyphandle(states[len / sizeof(uint32_t) - 1]);
free(states, M_TEMP, len);
if (node == 0)
return;
ci->ci_psci_suspend_param =
OF_getpropint(node, "arm,psci-suspend-param", 0);
}
void
cpu_psci_idle_cycle(void)
{
struct cpu_info *ci = curcpu();
struct timeval start, stop;
u_long itime;
microuptime(&start);
if (ci->ci_prev_sleep > ci->ci_psci_idle_latency)
psci_cpu_suspend(ci->ci_psci_idle_param, 0, 0);
else
cpu_wfi();
microuptime(&stop);
timersub(&stop, &start, &stop);
itime = stop.tv_sec * 1000000 + stop.tv_usec;
ci->ci_last_itime = itime;
itime >>= 1;
ci->ci_prev_sleep = (ci->ci_prev_sleep + (ci->ci_prev_sleep >> 1)
+ itime) >> 1;
}
#if NKSTAT > 0
struct cpu_kstats {
struct kstat_kv ck_impl;
struct kstat_kv ck_part;
struct kstat_kv ck_rev;
struct kstat_kv ck_capacity;
};
int
cpu_kstat_read(struct kstat *ks)
{
struct cpu_info *ci = ks->ks_softc;
struct cpu_kstats *ck = ks->ks_data;
kstat_kv_u64(&ck->ck_capacity) = ci->ci_capacity;
return 0;
}
void
cpu_kstat_attach(struct cpu_info *ci)
{
struct kstat *ks;
struct cpu_kstats *ck;
uint64_t impl, part;
const char *impl_name = NULL, *part_name = NULL;
const struct cpu_cores *coreselecter = cpu_cores_none;
int i;
ks = kstat_create(ci->ci_dev->dv_xname, 0, "mach", 0, KSTAT_T_KV, 0);
if (ks == NULL) {
printf("%s: unable to create cpu kstats\n",
ci->ci_dev->dv_xname);
return;
}
ck = malloc(sizeof(*ck), M_DEVBUF, M_WAITOK);
impl = CPU_IMPL(ci->ci_midr);
part = CPU_PART(ci->ci_midr);
for (i = 0; cpu_implementers[i].name; i++) {
if (impl == cpu_implementers[i].id) {
impl_name = cpu_implementers[i].name;
coreselecter = cpu_implementers[i].corelist;
break;
}
}
if (impl_name) {
kstat_kv_init(&ck->ck_impl, "impl", KSTAT_KV_T_ISTR);
strlcpy(kstat_kv_istr(&ck->ck_impl), impl_name,
sizeof(kstat_kv_istr(&ck->ck_impl)));
} else
kstat_kv_init(&ck->ck_impl, "impl", KSTAT_KV_T_NULL);
for (i = 0; coreselecter[i].name; i++) {
if (part == coreselecter[i].id) {
part_name = coreselecter[i].name;
break;
}
}
if (part_name) {
kstat_kv_init(&ck->ck_part, "part", KSTAT_KV_T_ISTR);
strlcpy(kstat_kv_istr(&ck->ck_part), part_name,
sizeof(kstat_kv_istr(&ck->ck_part)));
} else
kstat_kv_init(&ck->ck_part, "part", KSTAT_KV_T_NULL);
kstat_kv_init(&ck->ck_rev, "rev", KSTAT_KV_T_ISTR);
snprintf(kstat_kv_istr(&ck->ck_rev), sizeof(kstat_kv_istr(&ck->ck_rev)),
"r%llup%llu", CPU_VAR(ci->ci_midr), CPU_REV(ci->ci_midr));
kstat_kv_init(&ck->ck_capacity, "capacity", KSTAT_KV_T_UINT64);
ks->ks_softc = ci;
ks->ks_data = ck;
ks->ks_datalen = sizeof(*ck);
ks->ks_read = cpu_kstat_read;
kstat_install(ks);
}
struct cpu_opp_kstats {
struct kstat_kv coppk_freq;
struct kstat_kv coppk_supply_v;
};
int
cpu_opp_kstat_read(struct kstat *ks)
{
struct cpu_info *ci = ks->ks_softc;
struct cpu_opp_kstats *coppk = ks->ks_data;
struct opp_table *ot = ci->ci_opp_table;
struct cpu_info *oci;
struct timespec now, diff;
getnanouptime(&now);
timespecsub(&now, &ks->ks_updated, &diff);
if (diff.tv_sec < 1)
return (0);
if (ot == NULL)
return (0);
oci = ot->ot_master;
if (oci == NULL)
oci = ci;
kstat_kv_freq(&coppk->coppk_freq) =
clock_get_frequency(oci->ci_node, NULL);
if (oci->ci_cpu_supply) {
kstat_kv_volts(&coppk->coppk_supply_v) =
regulator_get_voltage(oci->ci_cpu_supply);
}
ks->ks_updated = now;
return (0);
}
void
cpu_opp_kstat_attach(struct cpu_info *ci)
{
struct kstat *ks;
struct cpu_opp_kstats *coppk;
struct opp_table *ot = ci->ci_opp_table;
struct cpu_info *oci = ot->ot_master;
if (oci == NULL)
oci = ci;
ks = kstat_create(ci->ci_dev->dv_xname, 0, "dt-opp", 0,
KSTAT_T_KV, 0);
if (ks == NULL) {
printf("%s: unable to create cpu dt-opp kstats\n",
ci->ci_dev->dv_xname);
return;
}
coppk = malloc(sizeof(*coppk), M_DEVBUF, M_WAITOK);
kstat_kv_init(&coppk->coppk_freq, "freq", KSTAT_KV_T_FREQ);
kstat_kv_init(&coppk->coppk_supply_v, "supply",
oci->ci_cpu_supply ? KSTAT_KV_T_VOLTS_DC : KSTAT_KV_T_NULL);
ks->ks_softc = oci;
ks->ks_data = coppk;
ks->ks_datalen = sizeof(*coppk);
ks->ks_read = cpu_opp_kstat_read;
kstat_install(ks);
}
#endif