aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@linux-mips.org>2014-04-06 16:31:29 -0400
committerRalf Baechle <ralf@linux-mips.org>2014-05-30 12:21:30 -0400
commit06947aaaf9bf7da0b29e211ee1a44f3ca91c08fa (patch)
treee9ace017ae9b79a8f2ad6c7f851f79ef97f1b4d1
parentfedfcb1137d2426e2e7a8ff40aa0ac4ec44793a9 (diff)
MIPS: Implement random_get_entropy with CP0 Random
Update to commit 9c9b415c50bc298ac61412dff856eae2f54889ee [MIPS: Reimplement get_cycles().] On systems were for whatever reasons we can't use the cycle counter, fall back to the c0_random register as an entropy source. It has however a very small range that makes it suitable for random_get_entropy only and not get_cycles. This optimised version compiles to 8 instructions in the fast path even in the worst case of all the conditions to check being variable (including a MFC0 move delay slot that is only required for very old processors): 828: 8cf90000 lw t9,0(a3) 828: R_MIPS_LO16 jiffies 82c: 40057800 mfc0 a1,c0_prid 830: 3c0200ff lui v0,0xff 834: 00a21024 and v0,a1,v0 838: 1040007d beqz v0,a30 <add_interrupt_randomness+0x22c> 83c: 3c030000 lui v1,0x0 83c: R_MIPS_HI16 cpu_data 840: 40024800 mfc0 v0,c0_count 844: 00000000 nop 848: 00409021 move s2,v0 84c: 8ce20000 lw v0,0(a3) 84c: R_MIPS_LO16 jiffies On most targets the sequence will be shorter and on some it will reduce to a single `MFC0 <reg>,c0_count', as all MIPS architecture (i.e. non-legacy MIPS) processors require the CP0 Count register to be present. The only known exception that reports MIPS architecture compliance, but contrary to that lacks CP0 Count is the Ingenic JZ4740 thingy. For broken platforms like that this code requires cpu_has_counter to be hardcoded to 0 (i.e. no variable setting is permitted) so as not to penalise all the other good platforms out there. The asm barrier is required so that the compiler does not pull any potentially costly (cold cache!) `cpu_data' variable access into the fast path. Signed-off-by: Ralf Baechle <ralf@linux-mips.org> Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org> Cc: Theodore Ts'o <tytso@mit.edu> Cc: John Crispin <blogic@openwrt.org> Cc: Andrew McGregor <andrewmcgr@gmail.com> Cc: Dave Taht <dave.taht@bufferbloat.net> Cc: Felix Fietkau <nbd@nbd.name> Cc: Simon Kelley <simon@thekelleys.org.uk> Cc: Jim Gettys <jg@freedesktop.org> Cc: David Daney <ddaney@caviumnetworks.com> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/6702/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--arch/mips/include/asm/timex.h65
-rw-r--r--arch/mips/kernel/cpu-probe.c1
2 files changed, 48 insertions, 18 deletions
diff --git a/arch/mips/include/asm/timex.h b/arch/mips/include/asm/timex.h
index c5424757da65..b05bb70a2e46 100644
--- a/arch/mips/include/asm/timex.h
+++ b/arch/mips/include/asm/timex.h
@@ -4,12 +4,16 @@
4 * for more details. 4 * for more details.
5 * 5 *
6 * Copyright (C) 1998, 1999, 2003 by Ralf Baechle 6 * Copyright (C) 1998, 1999, 2003 by Ralf Baechle
7 * Copyright (C) 2014 by Maciej W. Rozycki
7 */ 8 */
8#ifndef _ASM_TIMEX_H 9#ifndef _ASM_TIMEX_H
9#define _ASM_TIMEX_H 10#define _ASM_TIMEX_H
10 11
11#ifdef __KERNEL__ 12#ifdef __KERNEL__
12 13
14#include <linux/compiler.h>
15
16#include <asm/cpu.h>
13#include <asm/cpu-features.h> 17#include <asm/cpu-features.h>
14#include <asm/mipsregs.h> 18#include <asm/mipsregs.h>
15#include <asm/cpu-type.h> 19#include <asm/cpu-type.h>
@@ -45,29 +49,54 @@ typedef unsigned int cycles_t;
45 * However for now the implementaton of this function doesn't get these 49 * However for now the implementaton of this function doesn't get these
46 * fine details right. 50 * fine details right.
47 */ 51 */
48static inline cycles_t get_cycles(void) 52static inline int can_use_mips_counter(unsigned int prid)
49{ 53{
50 switch (boot_cpu_type()) { 54 int comp = (prid & PRID_COMP_MASK) != PRID_COMP_LEGACY;
51 case CPU_R4400PC:
52 case CPU_R4400SC:
53 case CPU_R4400MC:
54 if ((read_c0_prid() & 0xff) >= 0x0050)
55 return read_c0_count();
56 break;
57 55
58 case CPU_R4000PC: 56 if (__builtin_constant_p(cpu_has_counter) && !cpu_has_counter)
59 case CPU_R4000SC: 57 return 0;
60 case CPU_R4000MC: 58 else if (__builtin_constant_p(cpu_has_mips_r) && cpu_has_mips_r)
61 break; 59 return 1;
60 else if (likely(!__builtin_constant_p(cpu_has_mips_r) && comp))
61 return 1;
62 /* Make sure we don't peek at cpu_data[0].options in the fast path! */
63 if (!__builtin_constant_p(cpu_has_counter))
64 asm volatile("" : "=m" (cpu_data[0].options));
65 if (likely(cpu_has_counter &&
66 prid >= (PRID_IMP_R4000 | PRID_REV_ENCODE_44(5, 0))))
67 return 1;
68 else
69 return 0;
70}
62 71
63 default: 72static inline cycles_t get_cycles(void)
64 if (cpu_has_counter) 73{
65 return read_c0_count(); 74 if (can_use_mips_counter(read_c0_prid()))
66 break; 75 return read_c0_count();
67 } 76 else
77 return 0; /* no usable counter */
78}
79
80/*
81 * Like get_cycles - but where c0_count is not available we desperately
82 * use c0_random in an attempt to get at least a little bit of entropy.
83 *
84 * R6000 and R6000A neither have a count register nor a random register.
85 * That leaves no entropy source in the CPU itself.
86 */
87static inline unsigned long random_get_entropy(void)
88{
89 unsigned int prid = read_c0_prid();
90 unsigned int imp = prid & PRID_IMP_MASK;
68 91
69 return 0; /* no usable counter */ 92 if (can_use_mips_counter(prid))
93 return read_c0_count();
94 else if (likely(imp != PRID_IMP_R6000 && imp != PRID_IMP_R6000A))
95 return read_c0_random();
96 else
97 return 0; /* no usable register */
70} 98}
99#define random_get_entropy random_get_entropy
71 100
72#endif /* __KERNEL__ */ 101#endif /* __KERNEL__ */
73 102
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index f34000b27bf1..b1b580d69db0 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -1026,6 +1026,7 @@ static inline void cpu_probe_ingenic(struct cpuinfo_mips *c, unsigned int cpu)
1026 decode_configs(c); 1026 decode_configs(c);
1027 /* JZRISC does not implement the CP0 counter. */ 1027 /* JZRISC does not implement the CP0 counter. */
1028 c->options &= ~MIPS_CPU_COUNTER; 1028 c->options &= ~MIPS_CPU_COUNTER;
1029 BUG_ON(!__builtin_constant_p(cpu_has_counter) || cpu_has_counter);
1029 switch (c->processor_id & PRID_IMP_MASK) { 1030 switch (c->processor_id & PRID_IMP_MASK) {
1030 case PRID_IMP_JZRISC: 1031 case PRID_IMP_JZRISC:
1031 c->cputype = CPU_JZRISC; 1032 c->cputype = CPU_JZRISC;