diff options
-rw-r--r-- | arch/s390/crypto/arch_random.c | 103 | ||||
-rw-r--r-- | arch/s390/include/asm/archrandom.h | 13 |
2 files changed, 102 insertions, 14 deletions
diff --git a/arch/s390/crypto/arch_random.c b/arch/s390/crypto/arch_random.c index 8720e9203ecf..dd95cdbd22ce 100644 --- a/arch/s390/crypto/arch_random.c +++ b/arch/s390/crypto/arch_random.c | |||
@@ -2,14 +2,37 @@ | |||
2 | /* | 2 | /* |
3 | * s390 arch random implementation. | 3 | * s390 arch random implementation. |
4 | * | 4 | * |
5 | * Copyright IBM Corp. 2017 | 5 | * Copyright IBM Corp. 2017, 2018 |
6 | * Author(s): Harald Freudenberger <freude@de.ibm.com> | 6 | * Author(s): Harald Freudenberger |
7 | * | ||
8 | * The s390_arch_random_generate() function may be called from random.c | ||
9 | * in interrupt context. So this implementation does the best to be very | ||
10 | * fast. There is a buffer of random data which is asynchronously checked | ||
11 | * and filled by a workqueue thread. | ||
12 | * If there are enough bytes in the buffer the s390_arch_random_generate() | ||
13 | * just delivers these bytes. Otherwise false is returned until the | ||
14 | * worker thread refills the buffer. | ||
15 | * The worker fills the rng buffer by pulling fresh entropy from the | ||
16 | * high quality (but slow) true hardware random generator. This entropy | ||
17 | * is then spread over the buffer with an pseudo random generator PRNG. | ||
18 | * As the arch_get_random_seed_long() fetches 8 bytes and the calling | ||
19 | * function add_interrupt_randomness() counts this as 1 bit entropy the | ||
20 | * distribution needs to make sure there is in fact 1 bit entropy contained | ||
21 | * in 8 bytes of the buffer. The current values pull 32 byte entropy | ||
22 | * and scatter this into a 2048 byte buffer. So 8 byte in the buffer | ||
23 | * will contain 1 bit of entropy. | ||
24 | * The worker thread is rescheduled based on the charge level of the | ||
25 | * buffer but at least with 500 ms delay to avoid too much CPU consumption. | ||
26 | * So the max. amount of rng data delivered via arch_get_random_seed is | ||
27 | * limited to 4k bytes per second. | ||
7 | */ | 28 | */ |
8 | 29 | ||
9 | #include <linux/kernel.h> | 30 | #include <linux/kernel.h> |
10 | #include <linux/atomic.h> | 31 | #include <linux/atomic.h> |
11 | #include <linux/random.h> | 32 | #include <linux/random.h> |
33 | #include <linux/slab.h> | ||
12 | #include <linux/static_key.h> | 34 | #include <linux/static_key.h> |
35 | #include <linux/workqueue.h> | ||
13 | #include <asm/cpacf.h> | 36 | #include <asm/cpacf.h> |
14 | 37 | ||
15 | DEFINE_STATIC_KEY_FALSE(s390_arch_random_available); | 38 | DEFINE_STATIC_KEY_FALSE(s390_arch_random_available); |
@@ -17,11 +40,83 @@ DEFINE_STATIC_KEY_FALSE(s390_arch_random_available); | |||
17 | atomic64_t s390_arch_random_counter = ATOMIC64_INIT(0); | 40 | atomic64_t s390_arch_random_counter = ATOMIC64_INIT(0); |
18 | EXPORT_SYMBOL(s390_arch_random_counter); | 41 | EXPORT_SYMBOL(s390_arch_random_counter); |
19 | 42 | ||
43 | #define ARCH_REFILL_TICKS (HZ/2) | ||
44 | #define ARCH_PRNG_SEED_SIZE 32 | ||
45 | #define ARCH_RNG_BUF_SIZE 2048 | ||
46 | |||
47 | static DEFINE_SPINLOCK(arch_rng_lock); | ||
48 | static u8 *arch_rng_buf; | ||
49 | static unsigned int arch_rng_buf_idx; | ||
50 | |||
51 | static void arch_rng_refill_buffer(struct work_struct *); | ||
52 | static DECLARE_DELAYED_WORK(arch_rng_work, arch_rng_refill_buffer); | ||
53 | |||
54 | bool s390_arch_random_generate(u8 *buf, unsigned int nbytes) | ||
55 | { | ||
56 | /* lock rng buffer */ | ||
57 | if (!spin_trylock(&arch_rng_lock)) | ||
58 | return false; | ||
59 | |||
60 | /* try to resolve the requested amount of bytes from the buffer */ | ||
61 | arch_rng_buf_idx -= nbytes; | ||
62 | if (arch_rng_buf_idx < ARCH_RNG_BUF_SIZE) { | ||
63 | memcpy(buf, arch_rng_buf + arch_rng_buf_idx, nbytes); | ||
64 | atomic64_add(nbytes, &s390_arch_random_counter); | ||
65 | spin_unlock(&arch_rng_lock); | ||
66 | return true; | ||
67 | } | ||
68 | |||
69 | /* not enough bytes in rng buffer, refill is done asynchronously */ | ||
70 | spin_unlock(&arch_rng_lock); | ||
71 | |||
72 | return false; | ||
73 | } | ||
74 | EXPORT_SYMBOL(s390_arch_random_generate); | ||
75 | |||
76 | static void arch_rng_refill_buffer(struct work_struct *unused) | ||
77 | { | ||
78 | unsigned int delay = ARCH_REFILL_TICKS; | ||
79 | |||
80 | spin_lock(&arch_rng_lock); | ||
81 | if (arch_rng_buf_idx > ARCH_RNG_BUF_SIZE) { | ||
82 | /* buffer is exhausted and needs refill */ | ||
83 | u8 seed[ARCH_PRNG_SEED_SIZE]; | ||
84 | u8 prng_wa[240]; | ||
85 | /* fetch ARCH_PRNG_SEED_SIZE bytes of entropy */ | ||
86 | cpacf_trng(NULL, 0, seed, sizeof(seed)); | ||
87 | /* blow this entropy up to ARCH_RNG_BUF_SIZE with PRNG */ | ||
88 | memset(prng_wa, 0, sizeof(prng_wa)); | ||
89 | cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED, | ||
90 | &prng_wa, NULL, 0, seed, sizeof(seed)); | ||
91 | cpacf_prno(CPACF_PRNO_SHA512_DRNG_GEN, | ||
92 | &prng_wa, arch_rng_buf, ARCH_RNG_BUF_SIZE, NULL, 0); | ||
93 | arch_rng_buf_idx = ARCH_RNG_BUF_SIZE; | ||
94 | } | ||
95 | delay += (ARCH_REFILL_TICKS * arch_rng_buf_idx) / ARCH_RNG_BUF_SIZE; | ||
96 | spin_unlock(&arch_rng_lock); | ||
97 | |||
98 | /* kick next check */ | ||
99 | queue_delayed_work(system_long_wq, &arch_rng_work, delay); | ||
100 | } | ||
101 | |||
20 | static int __init s390_arch_random_init(void) | 102 | static int __init s390_arch_random_init(void) |
21 | { | 103 | { |
22 | /* check if subfunction CPACF_PRNO_TRNG is available */ | 104 | /* all the needed PRNO subfunctions available ? */ |
23 | if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) | 105 | if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG) && |
106 | cpacf_query_func(CPACF_PRNO, CPACF_PRNO_SHA512_DRNG_GEN)) { | ||
107 | |||
108 | /* alloc arch random working buffer */ | ||
109 | arch_rng_buf = kmalloc(ARCH_RNG_BUF_SIZE, GFP_KERNEL); | ||
110 | if (!arch_rng_buf) | ||
111 | return -ENOMEM; | ||
112 | |||
113 | /* kick worker queue job to fill the random buffer */ | ||
114 | queue_delayed_work(system_long_wq, | ||
115 | &arch_rng_work, ARCH_REFILL_TICKS); | ||
116 | |||
117 | /* enable arch random to the outside world */ | ||
24 | static_branch_enable(&s390_arch_random_available); | 118 | static_branch_enable(&s390_arch_random_available); |
119 | } | ||
25 | 120 | ||
26 | return 0; | 121 | return 0; |
27 | } | 122 | } |
diff --git a/arch/s390/include/asm/archrandom.h b/arch/s390/include/asm/archrandom.h index 09aed1095336..c67b82dfa558 100644 --- a/arch/s390/include/asm/archrandom.h +++ b/arch/s390/include/asm/archrandom.h | |||
@@ -15,16 +15,11 @@ | |||
15 | 15 | ||
16 | #include <linux/static_key.h> | 16 | #include <linux/static_key.h> |
17 | #include <linux/atomic.h> | 17 | #include <linux/atomic.h> |
18 | #include <asm/cpacf.h> | ||
19 | 18 | ||
20 | DECLARE_STATIC_KEY_FALSE(s390_arch_random_available); | 19 | DECLARE_STATIC_KEY_FALSE(s390_arch_random_available); |
21 | extern atomic64_t s390_arch_random_counter; | 20 | extern atomic64_t s390_arch_random_counter; |
22 | 21 | ||
23 | static void s390_arch_random_generate(u8 *buf, unsigned int nbytes) | 22 | bool s390_arch_random_generate(u8 *buf, unsigned int nbytes); |
24 | { | ||
25 | cpacf_trng(NULL, 0, buf, nbytes); | ||
26 | atomic64_add(nbytes, &s390_arch_random_counter); | ||
27 | } | ||
28 | 23 | ||
29 | static inline bool arch_has_random(void) | 24 | static inline bool arch_has_random(void) |
30 | { | 25 | { |
@@ -51,8 +46,7 @@ static inline bool arch_get_random_int(unsigned int *v) | |||
51 | static inline bool arch_get_random_seed_long(unsigned long *v) | 46 | static inline bool arch_get_random_seed_long(unsigned long *v) |
52 | { | 47 | { |
53 | if (static_branch_likely(&s390_arch_random_available)) { | 48 | if (static_branch_likely(&s390_arch_random_available)) { |
54 | s390_arch_random_generate((u8 *)v, sizeof(*v)); | 49 | return s390_arch_random_generate((u8 *)v, sizeof(*v)); |
55 | return true; | ||
56 | } | 50 | } |
57 | return false; | 51 | return false; |
58 | } | 52 | } |
@@ -60,8 +54,7 @@ static inline bool arch_get_random_seed_long(unsigned long *v) | |||
60 | static inline bool arch_get_random_seed_int(unsigned int *v) | 54 | static inline bool arch_get_random_seed_int(unsigned int *v) |
61 | { | 55 | { |
62 | if (static_branch_likely(&s390_arch_random_available)) { | 56 | if (static_branch_likely(&s390_arch_random_available)) { |
63 | s390_arch_random_generate((u8 *)v, sizeof(*v)); | 57 | return s390_arch_random_generate((u8 *)v, sizeof(*v)); |
64 | return true; | ||
65 | } | 58 | } |
66 | return false; | 59 | return false; |
67 | } | 60 | } |