diff --git a/sys/arm64/arm64/elf_machdep.c b/sys/arm64/arm64/elf_machdep.c --- a/sys/arm64/arm64/elf_machdep.c +++ b/sys/arm64/arm64/elf_machdep.c @@ -56,6 +56,9 @@ u_long __read_frequently elf_hwcap; u_long __read_frequently elf_hwcap2; +/* TODO: Move to a better location */ +u_long __read_frequently linux_elf_hwcap; +u_long __read_frequently linux_elf_hwcap2; struct arm64_addr_mask elf64_addr_mask; diff --git a/sys/arm64/arm64/identcpu.c b/sys/arm64/arm64/identcpu.c --- a/sys/arm64/arm64/identcpu.c +++ b/sys/arm64/arm64/identcpu.c @@ -33,10 +33,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -162,6 +164,7 @@ static struct cpu_desc *cpu_desc; static struct cpu_desc kern_cpu_desc; static struct cpu_desc user_cpu_desc; +static struct cpu_desc l_user_cpu_desc; static struct cpu_desc * get_cpu_desc(u_int cpu) @@ -275,10 +278,12 @@ }; #define MRS_TYPE_MASK 0xf +#define MRS_TYPE_FBSD_SHIFT 0 +#define MRS_TYPE_LNX_SHIFT 8 #define MRS_INVALID 0 #define MRS_EXACT 1 #define MRS_EXACT_VAL(x) (MRS_EXACT | ((x) << 4)) -#define MRS_EXACT_FIELD(x) ((x) >> 4) +#define MRS_EXACT_FIELD(x) (((x) >> 4) & 0xf) #define MRS_LOWER 2 struct mrs_field_value { @@ -341,17 +346,23 @@ u_int shift; }; -#define MRS_FIELD_HWCAP(_register, _name, _sign, _type, _values, _hwcap) \ +#define MRS_FIELD_HWCAP_SPLIT(_register, _name, _sign, _fbsd_type, \ + _lnx_type, _values, _hwcap) \ { \ .name = #_name, \ .sign = (_sign), \ - .type = (_type), \ + .type = ((_fbsd_type) << MRS_TYPE_FBSD_SHIFT) | \ + ((_lnx_type) << MRS_TYPE_LNX_SHIFT), \ .shift = _register ## _ ## _name ## _SHIFT, \ .mask = _register ## _ ## _name ## _MASK, \ .values = (_values), \ .hwcaps = (_hwcap), \ } +#define MRS_FIELD_HWCAP(_register, _name, _sign, _type, _values, _hwcap) \ + MRS_FIELD_HWCAP_SPLIT(_register, _name, _sign, _type, _type, \ + _values, _hwcap) + #define MRS_FIELD(_register, _name, _sign, _type, _values) \ MRS_FIELD_HWCAP(_register, _name, _sign, _type, _values, NULL) @@ -1911,7 +1922,10 @@ for (i = 0; i < nitems(user_regs); i++) { if (user_regs[i].CRm == CRm && user_regs[i].Op2 == Op2) { - value = CPU_DESC_FIELD(user_cpu_desc, i); + if (SV_CURPROC_ABI() == SV_ABI_FREEBSD) + value = CPU_DESC_FIELD(user_cpu_desc, i); + else + value = CPU_DESC_FIELD(l_user_cpu_desc, i); break; } } @@ -2064,12 +2078,32 @@ return (false); } +static uint64_t +update_special_reg_field(uint64_t user_reg, u_int type, uint64_t value, + u_int shift, bool sign) +{ + switch (type & MRS_TYPE_MASK) { + case MRS_EXACT: + user_reg &= ~(0xful << shift); + user_reg |= (uint64_t)MRS_EXACT_FIELD(type) << shift; + break; + case MRS_LOWER: + user_reg = update_lower_register(user_reg, value, shift, 4, + sign); + break; + default: + panic("Invalid field type: %d", type); + } + + return (user_reg); +} + void update_special_regs(u_int cpu) { struct cpu_desc *desc; const struct mrs_field *fields; - uint64_t user_reg, kern_reg, value; + uint64_t l_user_reg, user_reg, kern_reg, value; int i, j; if (cpu == 0) { @@ -2080,6 +2114,8 @@ ID_AA64PFR0_FP_NONE | ID_AA64PFR0_EL1_64 | ID_AA64PFR0_EL0_64; user_cpu_desc.id_aa64dfr0 = ID_AA64DFR0_DebugVer_8; + /* Create the Linux user visible cpu description */ + memcpy(&l_user_cpu_desc, &user_cpu_desc, sizeof(user_cpu_desc)); } desc = get_cpu_desc(cpu); @@ -2088,33 +2124,33 @@ if (cpu == 0) { kern_reg = value; user_reg = value; + l_user_reg = value; } else { kern_reg = CPU_DESC_FIELD(kern_cpu_desc, i); user_reg = CPU_DESC_FIELD(user_cpu_desc, i); + l_user_reg = CPU_DESC_FIELD(l_user_cpu_desc, i); } fields = user_regs[i].fields; for (j = 0; fields[j].type != 0; j++) { - switch (fields[j].type & MRS_TYPE_MASK) { - case MRS_EXACT: - user_reg &= ~(0xful << fields[j].shift); - user_reg |= - (uint64_t)MRS_EXACT_FIELD(fields[j].type) << - fields[j].shift; - break; - case MRS_LOWER: - user_reg = update_lower_register(user_reg, - value, fields[j].shift, 4, fields[j].sign); - break; - default: - panic("Invalid field type: %d", fields[j].type); - } + /* Update the FreeBSD userspace ID register view */ + user_reg = update_special_reg_field(user_reg, + fields[j].type >> MRS_TYPE_FBSD_SHIFT, value, + fields[j].shift, fields[j].sign); + + /* Update the Linux userspace ID register view */ + l_user_reg = update_special_reg_field(l_user_reg, + fields[j].type >> MRS_TYPE_LNX_SHIFT, value, + fields[j].shift, fields[j].sign); + + /* Update the kernel ID register view */ kern_reg = update_lower_register(kern_reg, value, fields[j].shift, 4, fields[j].sign); } CPU_DESC_FIELD(kern_cpu_desc, i) = kern_reg; CPU_DESC_FIELD(user_cpu_desc, i) = user_reg; + CPU_DESC_FIELD(l_user_cpu_desc, i) = l_user_reg; } } @@ -2148,7 +2184,8 @@ * Find the values to export to userspace as AT_HWCAP and AT_HWCAP2. */ static void -parse_cpu_features(bool is64bit, u_long *hwcap, u_long *hwcap2) +parse_cpu_features(bool is64bit, struct cpu_desc *cpu_desc, u_long *hwcap, + u_long *hwcap2) { const struct mrs_field_hwcap *hwcaps; const struct mrs_field *fields; @@ -2160,7 +2197,7 @@ if (user_regs[i].is64bit != is64bit) continue; - reg = CPU_DESC_FIELD(user_cpu_desc, i); + reg = CPU_DESC_FIELD(*cpu_desc, i); fields = user_regs[i].fields; for (j = 0; fields[j].type != 0; j++) { hwcaps = fields[j].hwcaps; @@ -2216,13 +2253,16 @@ } /* Find the values to export to userspace as AT_HWCAP and AT_HWCAP2 */ - parse_cpu_features(true, &elf_hwcap, &elf_hwcap2); + parse_cpu_features(true, &user_cpu_desc, &elf_hwcap, &elf_hwcap2); + parse_cpu_features(true, &l_user_cpu_desc, &linux_elf_hwcap, + &linux_elf_hwcap2); #ifdef COMPAT_FREEBSD32 - parse_cpu_features(false, &elf32_hwcap, &elf32_hwcap2); + parse_cpu_features(false, &user_cpu_desc, &elf32_hwcap, &elf32_hwcap2); #endif /* We export the CPUID registers */ elf_hwcap |= HWCAP_CPUID; + linux_elf_hwcap |= HWCAP_CPUID; #ifdef COMPAT_FREEBSD32 /* Set the default caps and any that need to check multiple fields */ diff --git a/sys/arm64/include/md_var.h b/sys/arm64/include/md_var.h --- a/sys/arm64/include/md_var.h +++ b/sys/arm64/include/md_var.h @@ -37,6 +37,8 @@ extern int szsigcode; extern u_long elf_hwcap; extern u_long elf_hwcap2; +extern u_long linux_elf_hwcap; +extern u_long linux_elf_hwcap2; #ifdef COMPAT_FREEBSD32 extern u_long elf32_hwcap; extern u_long elf32_hwcap2; diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c --- a/sys/arm64/linux/linux_sysvec.c +++ b/sys/arm64/linux/linux_sysvec.c @@ -456,8 +456,8 @@ .sv_schedtail = linux_schedtail, .sv_thread_detach = linux_thread_detach, .sv_trap = NULL, - .sv_hwcap = &elf_hwcap, - .sv_hwcap2 = &elf_hwcap2, + .sv_hwcap = &linux_elf_hwcap, + .sv_hwcap2 = &linux_elf_hwcap2, .sv_onexec = linux_on_exec_vmspace, .sv_onexit = linux_on_exit, .sv_ontdexit = linux_thread_dtor,