Changeset View
Changeset View
Standalone View
Standalone View
head/sys/arm64/arm64/identcpu.c
Show First 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | struct cpu_desc { | ||||
uint64_t id_aa64mmfr1; | uint64_t id_aa64mmfr1; | ||||
uint64_t id_aa64mmfr2; | uint64_t id_aa64mmfr2; | ||||
uint64_t id_aa64pfr0; | uint64_t id_aa64pfr0; | ||||
uint64_t id_aa64pfr1; | uint64_t id_aa64pfr1; | ||||
uint64_t ctr; | uint64_t ctr; | ||||
}; | }; | ||||
static struct cpu_desc cpu_desc[MAXCPU]; | static struct cpu_desc cpu_desc[MAXCPU]; | ||||
static struct cpu_desc kern_cpu_desc; | |||||
static struct cpu_desc user_cpu_desc; | static struct cpu_desc user_cpu_desc; | ||||
static u_int cpu_print_regs; | static u_int cpu_print_regs; | ||||
#define PRINT_ID_AA64_AFR0 0x00000001 | #define PRINT_ID_AA64_AFR0 0x00000001 | ||||
#define PRINT_ID_AA64_AFR1 0x00000002 | #define PRINT_ID_AA64_AFR1 0x00000002 | ||||
#define PRINT_ID_AA64_DFR0 0x00000010 | #define PRINT_ID_AA64_DFR0 0x00000010 | ||||
#define PRINT_ID_AA64_DFR1 0x00000020 | #define PRINT_ID_AA64_DFR1 0x00000020 | ||||
#define PRINT_ID_AA64_ISAR0 0x00000100 | #define PRINT_ID_AA64_ISAR0 0x00000100 | ||||
#define PRINT_ID_AA64_ISAR1 0x00000200 | #define PRINT_ID_AA64_ISAR1 0x00000200 | ||||
▲ Show 20 Lines • Show All 786 Lines • ▼ Show 20 Lines | if (user_regs[i].reg == reg) { | ||||
*val = value >> field_shift; | *val = value >> field_shift; | ||||
return (true); | return (true); | ||||
} | } | ||||
} | } | ||||
return (false); | return (false); | ||||
} | } | ||||
bool | |||||
get_kernel_reg(u_int reg, uint64_t *val) | |||||
{ | |||||
int i; | |||||
for (i = 0; i < nitems(user_regs); i++) { | |||||
if (user_regs[i].reg == reg) { | |||||
*val = CPU_DESC_FIELD(kern_cpu_desc, i); | |||||
return (true); | |||||
} | |||||
} | |||||
return (false); | |||||
} | |||||
static uint64_t | |||||
update_lower_register(uint64_t val, uint64_t new_val, u_int shift, | |||||
int width, bool sign) | |||||
{ | |||||
uint64_t mask; | |||||
uint64_t new_field, old_field; | |||||
bool update; | |||||
KASSERT(width > 0 && width < 64, ("%s: Invalid width %d", __func__, | |||||
width)); | |||||
mask = (1ul << width) - 1; | |||||
new_field = (new_val >> shift) & mask; | |||||
old_field = (val >> shift) & mask; | |||||
update = false; | |||||
if (sign) { | |||||
/* | |||||
* The field is signed. Toggle the upper bit so the comparison | |||||
* works on unsigned values as this makes positive numbers, | |||||
* i.e. those with a 0 bit, larger than negative numbers, | |||||
* i.e. those with a 1 bit, in an unsigned comparison. | |||||
*/ | |||||
if ((new_field ^ (1ul << (width - 1))) < | |||||
(old_field ^ (1ul << (width - 1)))) | |||||
update = true; | |||||
} else { | |||||
if (new_field < old_field) | |||||
update = true; | |||||
} | |||||
if (update) { | |||||
val &= ~(mask << shift); | |||||
val |= new_field << shift; | |||||
} | |||||
return (val); | |||||
} | |||||
static void | static void | ||||
update_user_regs(u_int cpu) | update_special_regs(u_int cpu) | ||||
{ | { | ||||
struct mrs_field *fields; | struct mrs_field *fields; | ||||
uint64_t cur, value; | uint64_t user_reg, kern_reg, value; | ||||
int i, j, cur_field, new_field; | int i, j; | ||||
if (cpu == 0) { | |||||
/* Create a user visible cpu description with safe values */ | |||||
memset(&user_cpu_desc, 0, sizeof(user_cpu_desc)); | |||||
/* Safe values for these registers */ | |||||
user_cpu_desc.id_aa64pfr0 = ID_AA64PFR0_AdvSIMD_NONE | | |||||
ID_AA64PFR0_FP_NONE | ID_AA64PFR0_EL1_64 | | |||||
ID_AA64PFR0_EL0_64; | |||||
user_cpu_desc.id_aa64dfr0 = ID_AA64DFR0_DebugVer_8; | |||||
} | |||||
for (i = 0; i < nitems(user_regs); i++) { | for (i = 0; i < nitems(user_regs); i++) { | ||||
value = CPU_DESC_FIELD(cpu_desc[cpu], i); | value = CPU_DESC_FIELD(cpu_desc[cpu], i); | ||||
if (cpu == 0) | if (cpu == 0) { | ||||
cur = value; | kern_reg = value; | ||||
else | user_reg = value; | ||||
cur = CPU_DESC_FIELD(user_cpu_desc, i); | } else { | ||||
kern_reg = CPU_DESC_FIELD(kern_cpu_desc, i); | |||||
user_reg = CPU_DESC_FIELD(user_cpu_desc, i); | |||||
} | |||||
fields = user_regs[i].fields; | fields = user_regs[i].fields; | ||||
for (j = 0; fields[j].type != 0; j++) { | for (j = 0; fields[j].type != 0; j++) { | ||||
switch (fields[j].type & MRS_TYPE_MASK) { | switch (fields[j].type & MRS_TYPE_MASK) { | ||||
case MRS_EXACT: | case MRS_EXACT: | ||||
cur &= ~(0xfu << fields[j].shift); | user_reg &= ~(0xfu << fields[j].shift); | ||||
cur |= | user_reg |= | ||||
(uint64_t)MRS_EXACT_FIELD(fields[j].type) << | (uint64_t)MRS_EXACT_FIELD(fields[j].type) << | ||||
fields[j].shift; | fields[j].shift; | ||||
break; | break; | ||||
case MRS_LOWER: | case MRS_LOWER: | ||||
new_field = (value >> fields[j].shift) & 0xf; | user_reg = update_lower_register(user_reg, | ||||
cur_field = (cur >> fields[j].shift) & 0xf; | value, fields[j].shift, 4, fields[j].sign); | ||||
if ((fields[j].sign && | |||||
(int)new_field < (int)cur_field) || | |||||
(!fields[j].sign && | |||||
(u_int)new_field < (u_int)cur_field)) { | |||||
cur &= ~(0xfu << fields[j].shift); | |||||
cur |= new_field << fields[j].shift; | |||||
} | |||||
break; | break; | ||||
default: | default: | ||||
panic("Invalid field type: %d", fields[j].type); | panic("Invalid field type: %d", fields[j].type); | ||||
} | } | ||||
kern_reg = update_lower_register(kern_reg, value, | |||||
fields[j].shift, 4, fields[j].sign); | |||||
} | } | ||||
CPU_DESC_FIELD(user_cpu_desc, i) = cur; | CPU_DESC_FIELD(kern_cpu_desc, i) = kern_reg; | ||||
CPU_DESC_FIELD(user_cpu_desc, i) = user_reg; | |||||
} | } | ||||
} | } | ||||
/* HWCAP */ | /* HWCAP */ | ||||
extern u_long elf_hwcap; | extern u_long elf_hwcap; | ||||
bool __read_frequently lse_supported = false; | bool __read_frequently lse_supported = false; | ||||
bool __read_frequently icache_aliasing = false; | bool __read_frequently icache_aliasing = false; | ||||
bool __read_frequently icache_vmid = false; | bool __read_frequently icache_vmid = false; | ||||
int64_t dcache_line_size; /* The minimum D cache line size */ | int64_t dcache_line_size; /* The minimum D cache line size */ | ||||
int64_t icache_line_size; /* The minimum I cache line size */ | int64_t icache_line_size; /* The minimum I cache line size */ | ||||
int64_t idcache_line_size; /* The minimum cache line size */ | int64_t idcache_line_size; /* The minimum cache line size */ | ||||
static void | static void | ||||
identify_cpu_sysinit(void *dummy __unused) | identify_cpu_sysinit(void *dummy __unused) | ||||
{ | { | ||||
int cpu; | int cpu; | ||||
u_long hwcap; | u_long hwcap; | ||||
bool dic, idc; | bool dic, idc; | ||||
/* Create a user visible cpu description with safe values */ | |||||
memset(&user_cpu_desc, 0, sizeof(user_cpu_desc)); | |||||
/* Safe values for these registers */ | |||||
user_cpu_desc.id_aa64pfr0 = ID_AA64PFR0_AdvSIMD_NONE | | |||||
ID_AA64PFR0_FP_NONE | ID_AA64PFR0_EL1_64 | ID_AA64PFR0_EL0_64; | |||||
user_cpu_desc.id_aa64dfr0 = ID_AA64DFR0_DebugVer_8; | |||||
dic = (allow_dic != 0); | dic = (allow_dic != 0); | ||||
idc = (allow_idc != 0); | idc = (allow_idc != 0); | ||||
CPU_FOREACH(cpu) { | CPU_FOREACH(cpu) { | ||||
print_cpu_features(cpu); | print_cpu_features(cpu); | ||||
hwcap = parse_cpu_features_hwcap(cpu); | hwcap = parse_cpu_features_hwcap(cpu); | ||||
if (elf_hwcap == 0) | if (elf_hwcap == 0) | ||||
elf_hwcap = hwcap; | elf_hwcap = hwcap; | ||||
else | else | ||||
elf_hwcap &= hwcap; | elf_hwcap &= hwcap; | ||||
update_user_regs(cpu); | update_special_regs(cpu); | ||||
if (CTR_DIC_VAL(cpu_desc[cpu].ctr) == 0) | if (CTR_DIC_VAL(cpu_desc[cpu].ctr) == 0) | ||||
dic = false; | dic = false; | ||||
if (CTR_IDC_VAL(cpu_desc[cpu].ctr) == 0) | if (CTR_IDC_VAL(cpu_desc[cpu].ctr) == 0) | ||||
idc = false; | idc = false; | ||||
} | } | ||||
if (dic && idc) { | if (dic && idc) { | ||||
▲ Show 20 Lines • Show All 500 Lines • Show Last 20 Lines |