Exporting all the different possible configurations of CPUID
registers to userspace via hwcaps is going to explode the hwcaps.
Emulate userspace cpuid register accesses and export a new
"cpuid" hwcap instead so that userspace can know to try to
read the cpuid registers itself.

Cc: Måns Rullgård <m...@mansr.com>
Cc: Will Deacon <will.dea...@arm.com>
Cc: Mark Rutland <mark.rutl...@arm.com>
Signed-off-by: Stephen Boyd <sb...@codeaurora.org>
---

This is in response to Will's suggestion[1] that we explore exposing
these cpuid registers to userspace. A simple test program is as follows:

  #include <stdio.h>
  
  int main(void)
  {
        int val;
  
        __asm__ volatile ("mrc p15, 0, %0, c0, c1, 0\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c1, 0 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c1, 1\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c1, 1 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c1, 2\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c1, 2 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c1, 3\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c1, 3 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c1, 4\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c1, 4 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c1, 5\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c1, 5 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c1, 6\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c1, 6 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c1, 7\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c1, 7 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c2, 0\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c2, 0 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c2, 1\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c2, 1 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c2, 2\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c2, 2 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c2, 3\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c2, 3 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c2, 4\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c2, 4 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c2, 5\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c2, 5 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c2, 6\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c2, 6 is %#x\n", val);
        __asm__ volatile ("mrc p15, 0, %0, c0, c2, 7\n" : "=r" (val));
        fprintf(stderr, "mrc p15, 0, rX, c0, c2, 7 is %#x\n", val);
        __asm__ volatile ("mrc p10, 7, %0, c7, c0, 0\n" : "=r" (val));
        fprintf(stderr, "mrc p10, 0, rX, c7, c0, 0 is %#x\n", val);
        __asm__ volatile ("mrc p10, 7, %0, c6, c0, 0\n" : "=r" (val));
        fprintf(stderr, "mrc p10, 0, rX, c6, c0, 0 is %#x\n", val);
        __asm__ volatile ("mrc p10, 7, %0, c0, c0, 0\n" : "=r" (val));
        fprintf(stderr, "mrc p10, 0, rX, c0, c0, 0 is %#x\n", val);
        __asm__ volatile ("mrc p10, 7, %0, c2, c0, 0\n" : "=r" (val));
  
        return 0;
  }

[1] http://lkml.kernel.org/r/20141027103118.ga8...@arm.com

 arch/arm/include/asm/opcodes.h    |   9 +++
 arch/arm/include/uapi/asm/hwcap.h |   1 +
 arch/arm/kernel/setup.c           | 113 +++++++++++++++++++++++++++++++++++++-
 arch/arm/kernel/swp_emulate.c     |   8 ---
 4 files changed, 122 insertions(+), 9 deletions(-)

diff --git a/arch/arm/include/asm/opcodes.h b/arch/arm/include/asm/opcodes.h
index e796c598513b..751eca1d4e22 100644
--- a/arch/arm/include/asm/opcodes.h
+++ b/arch/arm/include/asm/opcodes.h
@@ -216,6 +216,15 @@ extern asmlinkage unsigned int arm_check_condition(u32 
opcode, u32 psr);
 #define __inst_arm_thumb32(arm_opcode, thumb_opcode) __inst_arm(arm_opcode)
 #endif
 
+/*
+ * Macros/defines for extracting register numbers from instruction.
+ */
+#define EXTRACT_REG_NUM(instruction, offset) \
+       (((instruction) & (0xf << (offset))) >> (offset))
+#define RN_OFFSET  16
+#define RT_OFFSET  12
+#define RT2_OFFSET  0
+
 /* Helpers for the helpers.  Don't use these directly. */
 #ifdef __ASSEMBLY__
 #define ___inst_arm(x) .long x
diff --git a/arch/arm/include/uapi/asm/hwcap.h 
b/arch/arm/include/uapi/asm/hwcap.h
index 20d12f230a2f..4ec061e81d38 100644
--- a/arch/arm/include/uapi/asm/hwcap.h
+++ b/arch/arm/include/uapi/asm/hwcap.h
@@ -27,6 +27,7 @@
 #define HWCAP_IDIV     (HWCAP_IDIVA | HWCAP_IDIVT)
 #define HWCAP_LPAE     (1 << 20)
 #define HWCAP_EVTSTRM  (1 << 21)
+#define HWCAP_CPUID    (1 << 22)
 
 /*
  * HWCAP2 flags - for elf_hwcap2 (in kernel) and AT_HWCAP2
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 84db893dedc2..ea0c1a013e00 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -30,6 +30,7 @@
 #include <linux/bug.h>
 #include <linux/compiler.h>
 #include <linux/sort.h>
+#include <linux/perf_event.h>
 
 #include <asm/unified.h>
 #include <asm/cp15.h>
@@ -56,9 +57,11 @@
 #include <asm/unwind.h>
 #include <asm/memblock.h>
 #include <asm/virt.h>
+#include <asm/opcodes.h>
+#include <asm/vfp.h>
 
 #include "atags.h"
-
+#include "../vfp/vfpinstr.h"
 
 #if defined(CONFIG_FPE_NWFPE) || defined(CONFIG_FPE_FASTFPE)
 char fpe_type[8];
@@ -371,6 +374,108 @@ void __init early_print(const char *str, ...)
        printk("%s", buf);
 }
 
+static u32 arm_id_registers[2 * 8];
+static u32 arm_vfp_id_registers[3];
+
+static void cache_id_registers(void)
+{
+       arm_id_registers[0]  = read_cpuid_ext(CPUID_EXT_PFR0);
+       arm_id_registers[1]  = read_cpuid_ext(CPUID_EXT_PFR1);
+       arm_id_registers[2] = read_cpuid_ext(CPUID_EXT_DFR0);
+       arm_id_registers[3] = read_cpuid_ext(CPUID_EXT_AFR0);
+       arm_id_registers[4] = read_cpuid_ext(CPUID_EXT_MMFR0);
+       arm_id_registers[5] = read_cpuid_ext(CPUID_EXT_MMFR1);
+       arm_id_registers[6] = read_cpuid_ext(CPUID_EXT_MMFR2);
+       arm_id_registers[7] = read_cpuid_ext(CPUID_EXT_MMFR3);
+       arm_id_registers[8] = read_cpuid_ext(CPUID_EXT_ISAR0);
+       arm_id_registers[9] = read_cpuid_ext(CPUID_EXT_ISAR1);
+       arm_id_registers[10] = read_cpuid_ext(CPUID_EXT_ISAR2);
+       arm_id_registers[11] = read_cpuid_ext(CPUID_EXT_ISAR3);
+       arm_id_registers[12] = read_cpuid_ext(CPUID_EXT_ISAR4);
+       arm_id_registers[13] = read_cpuid_ext(CPUID_EXT_ISAR5);
+       arm_vfp_id_registers[0] = fmrx(FPSID);
+       arm_vfp_id_registers[1] = fmrx(MVFR1);
+       arm_vfp_id_registers[2] = fmrx(MVFR0);
+}
+
+static int get_id_trap(struct pt_regs *regs, unsigned int instr)
+{
+       unsigned int destreg, crm, opc2;
+       unsigned int res;
+
+       perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc);
+
+       res = arm_check_condition(instr, regs->ARM_cpsr);
+       if (res == ARM_OPCODE_CONDTEST_FAIL) {
+               regs->ARM_pc += 4;
+               return 0;
+       }
+
+       destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
+       crm = instr & 0xf;
+       opc2 = (instr >> 5) & 0x7;
+
+       if (crm > 2 || crm == 0)
+               return -EINVAL;
+
+       regs->uregs[destreg] = arm_id_registers[((crm - 1) * 8) + opc2];
+       regs->ARM_pc += 4;
+
+       return 0;
+}
+
+struct undef_hook arm_mrc_id_hook = {
+       .instr_mask     = 0x0f100f10,
+       .instr_val      = 0x0e100f10,
+       .cpsr_mask      = MODE_MASK,
+       .cpsr_val       = USR_MODE,
+       .fn             = get_id_trap,
+};
+
+static int get_vmrs_id_trap(struct pt_regs *regs, unsigned int instr)
+{
+       unsigned int destreg, data, reg;
+       unsigned int res;
+
+       perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc);
+
+       res = arm_check_condition(instr, regs->ARM_cpsr);
+       if (res == ARM_OPCODE_CONDTEST_FAIL) {
+               regs->ARM_pc += 4;
+               return 0;
+       }
+
+       destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
+       reg = (instr >> 16) & 0xf;
+
+       switch (reg) {
+       case 0:
+               data = arm_vfp_id_registers[0];
+               break;
+       case 6:
+               data = arm_vfp_id_registers[1];
+               break;
+       case 7:
+               data = arm_vfp_id_registers[2];
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regs->uregs[destreg] = data;
+       regs->ARM_pc += 4;
+
+       return 0;
+}
+
+struct undef_hook arm_vmrs_id_hook = {
+       .instr_mask     = 0x0ff00ff0,
+       .instr_val      = 0x0ef00a10,
+       .cpsr_mask      = MODE_MASK,
+       .cpsr_val       = USR_MODE,
+       .fn             = get_vmrs_id_trap,
+};
+
 static void __init cpuid_init_hwcaps(void)
 {
        unsigned int divide_instrs, vmsa;
@@ -378,6 +483,11 @@ static void __init cpuid_init_hwcaps(void)
        if (cpu_architecture() < CPU_ARCH_ARMv7)
                return;
 
+       cache_id_registers();
+       elf_hwcap |= HWCAP_CPUID;
+       register_undef_hook(&arm_mrc_id_hook);
+       register_undef_hook(&arm_vmrs_id_hook);
+
        divide_instrs = (read_cpuid_ext(CPUID_EXT_ISAR0) & 0x0f000000) >> 24;
 
        switch (divide_instrs) {
@@ -1009,6 +1119,7 @@ static const char *hwcap_str[] = {
        "vfpd32",
        "lpae",
        "evtstrm",
+       "cpuid",
        NULL
 };
 
diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c
index 67ca8578c6d8..ebdcac50feef 100644
--- a/arch/arm/kernel/swp_emulate.c
+++ b/arch/arm/kernel/swp_emulate.c
@@ -62,14 +62,6 @@
        __user_swpX_asm(data, addr, res, temp, "b")
 
 /*
- * Macros/defines for extracting register numbers from instruction.
- */
-#define EXTRACT_REG_NUM(instruction, offset) \
-       (((instruction) & (0xf << (offset))) >> (offset))
-#define RN_OFFSET  16
-#define RT_OFFSET  12
-#define RT2_OFFSET  0
-/*
  * Bit 22 of the instruction encoding distinguishes between
  * the SWP and SWPB variants (bit set means SWPB).
  */
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to