From babf31dfa044bfa5c97c3b83fea69c419d782a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 15 Feb 2019 17:27:27 -0500 Subject: [PATCH] target/arm: expose CPUID registers to userspace A number of CPUID registers are exposed to userspace by modern Linux kernels thanks to the "ARM64 CPU Feature Registers" ABI. For QEMU's user-mode emulation we don't need to emulate the kernels trap but just return the value the trap would have done. To avoid too much #ifdef hackery we process ARMCPRegInfo with a new helper (modify_arm_cp_regs) before defining the registers. The modify routine is driven by a simple data structure which describes which bits are exported and which are fixed. Backports commit 6c5c0fec29bbfe36c64eca1edfd8455be46b77c6 from qemu --- qemu/aarch64.h | 1 + qemu/aarch64eb.h | 1 + qemu/arm.h | 1 + qemu/armeb.h | 1 + qemu/header_gen.py | 1 + qemu/m68k.h | 1 + qemu/mips.h | 1 + qemu/mips64.h | 1 + qemu/mips64el.h | 1 + qemu/mipsel.h | 1 + qemu/powerpc.h | 1 + qemu/sparc.h | 1 + qemu/sparc64.h | 1 + qemu/target/arm/cpu.h | 21 ++++++++++++++ qemu/target/arm/helper.c | 59 ++++++++++++++++++++++++++++++++++++++++ qemu/x86_64.h | 1 + 16 files changed, 94 insertions(+) diff --git a/qemu/aarch64.h b/qemu/aarch64.h index 20be3f51..6d1b8df4 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_aarch64 #define memory_try_enable_merging memory_try_enable_merging_aarch64 #define memory_unmap memory_unmap_aarch64 +#define modify_arm_cp_regs modify_arm_cp_regs_aarch64 #define module_call_init module_call_init_aarch64 #define module_load module_load_aarch64 #define mpidr_cp_reginfo mpidr_cp_reginfo_aarch64 diff --git a/qemu/aarch64eb.h b/qemu/aarch64eb.h index ce8f5e23..93922a3a 100644 --- a/qemu/aarch64eb.h +++ b/qemu/aarch64eb.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_aarch64eb #define memory_try_enable_merging memory_try_enable_merging_aarch64eb #define memory_unmap memory_unmap_aarch64eb +#define modify_arm_cp_regs modify_arm_cp_regs_aarch64eb #define module_call_init module_call_init_aarch64eb #define module_load module_load_aarch64eb #define mpidr_cp_reginfo mpidr_cp_reginfo_aarch64eb diff --git a/qemu/arm.h b/qemu/arm.h index e8408e27..55d5a68d 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_arm #define memory_try_enable_merging memory_try_enable_merging_arm #define memory_unmap memory_unmap_arm +#define modify_arm_cp_regs modify_arm_cp_regs_arm #define module_call_init module_call_init_arm #define module_load module_load_arm #define mpidr_cp_reginfo mpidr_cp_reginfo_arm diff --git a/qemu/armeb.h b/qemu/armeb.h index c6d0afe9..e271b713 100644 --- a/qemu/armeb.h +++ b/qemu/armeb.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_armeb #define memory_try_enable_merging memory_try_enable_merging_armeb #define memory_unmap memory_unmap_armeb +#define modify_arm_cp_regs modify_arm_cp_regs_armeb #define module_call_init module_call_init_armeb #define module_load module_load_armeb #define mpidr_cp_reginfo mpidr_cp_reginfo_armeb diff --git a/qemu/header_gen.py b/qemu/header_gen.py index b57325b4..1a589c5c 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -2046,6 +2046,7 @@ symbols = ( 'memory_register_types', 'memory_try_enable_merging', 'memory_unmap', + 'modify_arm_cp_regs', 'module_call_init', 'module_load', 'mpidr_cp_reginfo', diff --git a/qemu/m68k.h b/qemu/m68k.h index 721a3830..4286fe66 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_m68k #define memory_try_enable_merging memory_try_enable_merging_m68k #define memory_unmap memory_unmap_m68k +#define modify_arm_cp_regs modify_arm_cp_regs_m68k #define module_call_init module_call_init_m68k #define module_load module_load_m68k #define mpidr_cp_reginfo mpidr_cp_reginfo_m68k diff --git a/qemu/mips.h b/qemu/mips.h index 87f72033..5af1bac7 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_mips #define memory_try_enable_merging memory_try_enable_merging_mips #define memory_unmap memory_unmap_mips +#define modify_arm_cp_regs modify_arm_cp_regs_mips #define module_call_init module_call_init_mips #define module_load module_load_mips #define mpidr_cp_reginfo mpidr_cp_reginfo_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 9078ebd9..b07516ff 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_mips64 #define memory_try_enable_merging memory_try_enable_merging_mips64 #define memory_unmap memory_unmap_mips64 +#define modify_arm_cp_regs modify_arm_cp_regs_mips64 #define module_call_init module_call_init_mips64 #define module_load module_load_mips64 #define mpidr_cp_reginfo mpidr_cp_reginfo_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 35e110bf..3722b353 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_mips64el #define memory_try_enable_merging memory_try_enable_merging_mips64el #define memory_unmap memory_unmap_mips64el +#define modify_arm_cp_regs modify_arm_cp_regs_mips64el #define module_call_init module_call_init_mips64el #define module_load module_load_mips64el #define mpidr_cp_reginfo mpidr_cp_reginfo_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 693de7c3..dcc83c0c 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_mipsel #define memory_try_enable_merging memory_try_enable_merging_mipsel #define memory_unmap memory_unmap_mipsel +#define modify_arm_cp_regs modify_arm_cp_regs_mipsel #define module_call_init module_call_init_mipsel #define module_load module_load_mipsel #define mpidr_cp_reginfo mpidr_cp_reginfo_mipsel diff --git a/qemu/powerpc.h b/qemu/powerpc.h index 9e6e5638..c8520bff 100644 --- a/qemu/powerpc.h +++ b/qemu/powerpc.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_powerpc #define memory_try_enable_merging memory_try_enable_merging_powerpc #define memory_unmap memory_unmap_powerpc +#define modify_arm_cp_regs modify_arm_cp_regs_powerpc #define module_call_init module_call_init_powerpc #define module_load module_load_powerpc #define mpidr_cp_reginfo mpidr_cp_reginfo_powerpc diff --git a/qemu/sparc.h b/qemu/sparc.h index 51c4ba4f..72f61716 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_sparc #define memory_try_enable_merging memory_try_enable_merging_sparc #define memory_unmap memory_unmap_sparc +#define modify_arm_cp_regs modify_arm_cp_regs_sparc #define module_call_init module_call_init_sparc #define module_load module_load_sparc #define mpidr_cp_reginfo mpidr_cp_reginfo_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index e14a89c9..22e6ee60 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_sparc64 #define memory_try_enable_merging memory_try_enable_merging_sparc64 #define memory_unmap memory_unmap_sparc64 +#define modify_arm_cp_regs modify_arm_cp_regs_sparc64 #define module_call_init module_call_init_sparc64 #define module_load module_load_sparc64 #define mpidr_cp_reginfo mpidr_cp_reginfo_sparc64 diff --git a/qemu/target/arm/cpu.h b/qemu/target/arm/cpu.h index 10c02e3e..29c823ba 100644 --- a/qemu/target/arm/cpu.h +++ b/qemu/target/arm/cpu.h @@ -2406,6 +2406,27 @@ static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs) } const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp); +/* + * Definition of an ARM co-processor register as viewed from + * userspace. This is used for presenting sanitised versions of + * registers to userspace when emulating the Linux AArch64 CPU + * ID/feature ABI (advertised as HWCAP_CPUID). + */ +typedef struct ARMCPRegUserSpaceInfo { + /* Name of register */ + const char *name; + + /* Only some bits are exported to user space */ + uint64_t exported_bits; + + /* Fixed bits are applied after the mask */ + uint64_t fixed_bits; +} ARMCPRegUserSpaceInfo; + +#define REGUSERINFO_SENTINEL { .name = NULL } + +void modify_arm_cp_regs(ARMCPRegInfo *regs, const ARMCPRegUserSpaceInfo *mods); + /* CPWriteFn that can be used to implement writes-ignored behaviour */ void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); diff --git a/qemu/target/arm/helper.c b/qemu/target/arm/helper.c index 27db5c00..7547d0b0 100644 --- a/qemu/target/arm/helper.c +++ b/qemu/target/arm/helper.c @@ -5290,6 +5290,30 @@ void register_cp_regs_for_features(ARMCPU *cpu) pmreg_access }, REGINFO_SENTINEL }; +#ifdef CONFIG_USER_ONLY + ARMCPRegUserSpaceInfo v8_user_idregs[] = { + { .name = "ID_AA64PFR0_EL1", + .exported_bits = 0x000f000f00ff0000, + .fixed_bits = 0x0000000000000011 }, + { .name = "ID_AA64PFR1_EL1", + .exported_bits = 0x00000000000000f0 }, + { .name = "ID_AA64ZFR0_EL1" }, + { .name = "ID_AA64MMFR0_EL1", + .fixed_bits = 0x00000000ff000000 }, + { .name = "ID_AA64MMFR1_EL1" }, + { .name = "ID_AA64DFR0_EL1", + .fixed_bits = 0x0000000000000006 }, + { .name = "ID_AA64DFR1_EL1" }, + { .name = "ID_AA64AFR0_EL1" }, + { .name = "ID_AA64AFR1_EL1" }, + { .name = "ID_AA64ISAR0_EL1", + .exported_bits = 0x00fffffff0fffff0 }, + { .name = "ID_AA64ISAR1_EL1", + .exported_bits = 0x000000f0ffffffff }, + REGUSERINFO_SENTINEL + }; + modify_arm_cp_regs(v8_idregs, v8_user_idregs); +#endif /* RVBAR_EL1 is only implemented if EL1 is the highest EL */ if (!arm_feature(env, ARM_FEATURE_EL3) && !arm_feature(env, ARM_FEATURE_EL2)) { @@ -5524,6 +5548,15 @@ void register_cp_regs_for_features(ARMCPU *cpu) "CRN0_WI", 15,0,CP_ANY, 0,CP_ANY,CP_ANY, 0, ARM_CP_NOP | ARM_CP_OVERRIDE, PL1_W, }; +#ifdef CONFIG_USER_ONLY + ARMCPRegUserSpaceInfo id_v8_user_midr_cp_reginfo[] = { + { .name = "MIDR_EL1", + .exported_bits = 0x00000000ffffffff }, + { .name = "REVIDR_EL1" }, + REGUSERINFO_SENTINEL + }; + modify_arm_cp_regs(id_v8_midr_cp_reginfo, id_v8_user_midr_cp_reginfo); +#endif if (arm_feature(env, ARM_FEATURE_OMAPCP) || arm_feature(env, ARM_FEATURE_STRONGARM)) { ARMCPRegInfo *r; @@ -6046,6 +6079,32 @@ void define_arm_cp_regs_with_opaque(ARMCPU *cpu, } } +/* + * Modify ARMCPRegInfo for access from userspace. + * + * This is a data driven modification directed by + * ARMCPRegUserSpaceInfo. All registers become ARM_CP_CONST as + * user-space cannot alter any values and dynamic values pertaining to + * execution state are hidden from user space view anyway. + */ +void modify_arm_cp_regs(ARMCPRegInfo *regs, const ARMCPRegUserSpaceInfo *mods) +{ + const ARMCPRegUserSpaceInfo *m; + ARMCPRegInfo *r; + + for (m = mods; m->name; m++) { + for (r = regs; r->type != ARM_CP_SENTINEL; r++) { + if (strcmp(r->name, m->name) == 0) { + r->type = ARM_CP_CONST; + r->access = PL0U_R; + r->resetvalue &= m->exported_bits; + r->resetvalue |= m->fixed_bits; + break; + } + } + } +} + const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp) { return g_hash_table_lookup(cpregs, &encoded_cp); diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 34132feb..a57dc2be 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -2040,6 +2040,7 @@ #define memory_register_types memory_register_types_x86_64 #define memory_try_enable_merging memory_try_enable_merging_x86_64 #define memory_unmap memory_unmap_x86_64 +#define modify_arm_cp_regs modify_arm_cp_regs_x86_64 #define module_call_init module_call_init_x86_64 #define module_load module_load_x86_64 #define mpidr_cp_reginfo mpidr_cp_reginfo_x86_64