-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathlrng_es_timer_common.c
144 lines (119 loc) · 3.47 KB
/
lrng_es_timer_common.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* LRNG Slow Entropy Source: Interrupt data collection
*
* Copyright (C) 2022, Stephan Mueller <[email protected]>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/gcd.h>
#include <linux/module.h>
#include "lrng_es_irq.h"
#include "lrng_es_sched.h"
#include "lrng_es_timer_common.h"
#include "lrng_health.h"
/* Is high-resolution timer present? */
static bool lrng_highres_timer_val = false;
/* Number of time stamps analyzed to calculate a GCD */
#define LRNG_GCD_WINDOW_SIZE 100
static u32 lrng_gcd_history[LRNG_GCD_WINDOW_SIZE];
static atomic_t lrng_gcd_history_ptr = ATOMIC_INIT(-1);
/* The common divisor for all timestamps */
static u32 lrng_gcd_timer = 0;
bool lrng_gcd_tested(void)
{
return (lrng_gcd_timer != 0);
}
u32 lrng_gcd_get(void)
{
return lrng_gcd_timer;
}
/* Set the GCD for use in IRQ ES - if 0, the GCD calculation is restarted. */
void lrng_gcd_set(u32 running_gcd)
{
lrng_gcd_timer = running_gcd;
/* Ensure that update to global variable lrng_gcd_timer is visible */
mb();
}
static void lrng_gcd_set_check(u32 running_gcd)
{
if (!lrng_gcd_tested()) {
lrng_gcd_set(running_gcd);
pr_debug("Setting GCD to %u\n", running_gcd);
}
}
u32 lrng_gcd_analyze(u32 *history, size_t nelem)
{
u32 running_gcd = 0;
size_t i;
/* Now perform the analysis on the accumulated time data. */
for (i = 0; i < nelem; i++) {
/*
* NOTE: this would be the place to add more analysis on the
* appropriateness of the timer like checking the presence
* of sufficient variations in the timer.
*/
/*
* This calculates the gcd of all the time values. that is
* gcd(time_1, time_2, ..., time_nelem)
*
* Some timers increment by a fixed (non-1) amount each step.
* This code checks for such increments, and allows the library
* to output the number of such changes have occurred.
*/
running_gcd = (u32)gcd(history[i], running_gcd);
/* Zeroize data */
history[i] = 0;
}
return running_gcd;
}
void lrng_gcd_add_value(u32 time)
{
u32 ptr = (u32)atomic_inc_return_relaxed(&lrng_gcd_history_ptr);
if (ptr < LRNG_GCD_WINDOW_SIZE) {
lrng_gcd_history[ptr] = time;
} else if (ptr == LRNG_GCD_WINDOW_SIZE) {
u32 gcd = lrng_gcd_analyze(lrng_gcd_history,
LRNG_GCD_WINDOW_SIZE);
if (!gcd)
gcd = 1;
/*
* Ensure that we have variations in the time stamp below the
* given value. This is just a safety measure to prevent the GCD
* becoming too large.
*/
if (gcd >= 1000) {
pr_warn("calculated GCD is larger than expected: %u\n",
gcd);
gcd = 1000;
}
/* Adjust all deltas by the observed (small) common factor. */
lrng_gcd_set_check(gcd);
atomic_set(&lrng_gcd_history_ptr, 0);
}
}
/* Return boolean whether LRNG identified presence of high-resolution timer */
bool lrng_highres_timer(void)
{
return lrng_highres_timer_val;
}
static int __init lrng_init_time_source(void)
{
if ((random_get_entropy() & LRNG_DATA_SLOTSIZE_MASK) ||
(random_get_entropy() & LRNG_DATA_SLOTSIZE_MASK)) {
/*
* As the highres timer is identified here, previous interrupts
* obtained during boot time are treated like a lowres-timer
* would have been present.
*/
lrng_highres_timer_val = true;
} else {
lrng_health_disable();
lrng_highres_timer_val = false;
}
lrng_irq_es_init(lrng_highres_timer_val);
lrng_sched_es_init(lrng_highres_timer_val);
/* Ensure that changes to global variables are visible */
mb();
return 0;
}
core_initcall(lrng_init_time_source);