Create a kernel arm64 ID register view
In preparation for using ifuncs in the kernel is is useful to have a common view of the arm64 ID registers across all CPUs. Add this and extract the logic for finding the lower value of two fields to a new helper function. Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D25463
This commit is contained in:
parent
fa9012aef1
commit
45e999d918
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=362777
@ -134,6 +134,7 @@ struct cpu_desc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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
|
||||||
@ -936,46 +937,109 @@ extract_user_id_field(u_int reg, u_int field_shift, uint8_t *val)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -997,13 +1061,6 @@ identify_cpu_sysinit(void *dummy __unused)
|
|||||||
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) {
|
||||||
@ -1013,7 +1070,7 @@ identify_cpu_sysinit(void *dummy __unused)
|
|||||||
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;
|
||||||
|
@ -62,7 +62,10 @@ void undef_init(void);
|
|||||||
void *install_undef_handler(bool, undef_handler_t);
|
void *install_undef_handler(bool, undef_handler_t);
|
||||||
void remove_undef_handler(void *);
|
void remove_undef_handler(void *);
|
||||||
int undef_insn(u_int, struct trapframe *);
|
int undef_insn(u_int, struct trapframe *);
|
||||||
|
|
||||||
|
/* Functions to read the sanitised view of the special registers */
|
||||||
bool extract_user_id_field(u_int, u_int, uint8_t *);
|
bool extract_user_id_field(u_int, u_int, uint8_t *);
|
||||||
|
bool get_kernel_reg(u_int, uint64_t *);
|
||||||
|
|
||||||
#endif /* _KERNEL */
|
#endif /* _KERNEL */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user