diff options
author | Michael Buesch <mb@bu3sch.de> | 2006-06-26 03:25:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 12:58:19 -0400 |
commit | 13523363577d49b9af3cad06fcb757126bedc61b (patch) | |
tree | c553841a21aee8c375a261a6f30680b552b7c034 /drivers/char/hw_random/via-rng.c | |
parent | ef5d862734b84239e0140319a95fb0bbff5ef394 (diff) |
[PATCH] Add VIA HW RNG driver
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/hw_random/via-rng.c')
-rw-r--r-- | drivers/char/hw_random/via-rng.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c new file mode 100644 index 000000000000..0e786b617bb8 --- /dev/null +++ b/drivers/char/hw_random/via-rng.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * RNG driver for VIA RNGs | ||
3 | * | ||
4 | * Copyright 2005 (c) MontaVista Software, Inc. | ||
5 | * | ||
6 | * with the majority of the code coming from: | ||
7 | * | ||
8 | * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) | ||
9 | * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com> | ||
10 | * | ||
11 | * derived from | ||
12 | * | ||
13 | * Hardware driver for the AMD 768 Random Number Generator (RNG) | ||
14 | * (c) Copyright 2001 Red Hat Inc <alan@redhat.com> | ||
15 | * | ||
16 | * derived from | ||
17 | * | ||
18 | * Hardware driver for Intel i810 Random Number Generator (RNG) | ||
19 | * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com> | ||
20 | * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> | ||
21 | * | ||
22 | * This file is licensed under the terms of the GNU General Public | ||
23 | * License version 2. This program is licensed "as is" without any | ||
24 | * warranty of any kind, whether express or implied. | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/hw_random.h> | ||
31 | #include <asm/io.h> | ||
32 | #include <asm/msr.h> | ||
33 | #include <asm/cpufeature.h> | ||
34 | |||
35 | |||
36 | #define PFX KBUILD_MODNAME ": " | ||
37 | |||
38 | |||
39 | enum { | ||
40 | VIA_STRFILT_CNT_SHIFT = 16, | ||
41 | VIA_STRFILT_FAIL = (1 << 15), | ||
42 | VIA_STRFILT_ENABLE = (1 << 14), | ||
43 | VIA_RAWBITS_ENABLE = (1 << 13), | ||
44 | VIA_RNG_ENABLE = (1 << 6), | ||
45 | VIA_XSTORE_CNT_MASK = 0x0F, | ||
46 | |||
47 | VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */ | ||
48 | VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */ | ||
49 | VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF, | ||
50 | VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */ | ||
51 | VIA_RNG_CHUNK_2_MASK = 0xFFFF, | ||
52 | VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */ | ||
53 | VIA_RNG_CHUNK_1_MASK = 0xFF, | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * Investigate using the 'rep' prefix to obtain 32 bits of random data | ||
58 | * in one insn. The upside is potentially better performance. The | ||
59 | * downside is that the instruction becomes no longer atomic. Due to | ||
60 | * this, just like familiar issues with /dev/random itself, the worst | ||
61 | * case of a 'rep xstore' could potentially pause a cpu for an | ||
62 | * unreasonably long time. In practice, this condition would likely | ||
63 | * only occur when the hardware is failing. (or so we hope :)) | ||
64 | * | ||
65 | * Another possible performance boost may come from simply buffering | ||
66 | * until we have 4 bytes, thus returning a u32 at a time, | ||
67 | * instead of the current u8-at-a-time. | ||
68 | */ | ||
69 | |||
70 | static inline u32 xstore(u32 *addr, u32 edx_in) | ||
71 | { | ||
72 | u32 eax_out; | ||
73 | |||
74 | asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" | ||
75 | :"=m"(*addr), "=a"(eax_out) | ||
76 | :"D"(addr), "d"(edx_in)); | ||
77 | |||
78 | return eax_out; | ||
79 | } | ||
80 | |||
81 | static int via_rng_data_present(struct hwrng *rng) | ||
82 | { | ||
83 | u32 bytes_out; | ||
84 | u32 *via_rng_datum = (u32 *)(&rng->priv); | ||
85 | |||
86 | /* We choose the recommended 1-byte-per-instruction RNG rate, | ||
87 | * for greater randomness at the expense of speed. Larger | ||
88 | * values 2, 4, or 8 bytes-per-instruction yield greater | ||
89 | * speed at lesser randomness. | ||
90 | * | ||
91 | * If you change this to another VIA_CHUNK_n, you must also | ||
92 | * change the ->n_bytes values in rng_vendor_ops[] tables. | ||
93 | * VIA_CHUNK_8 requires further code changes. | ||
94 | * | ||
95 | * A copy of MSR_VIA_RNG is placed in eax_out when xstore | ||
96 | * completes. | ||
97 | */ | ||
98 | |||
99 | *via_rng_datum = 0; /* paranoia, not really necessary */ | ||
100 | bytes_out = xstore(via_rng_datum, VIA_RNG_CHUNK_1); | ||
101 | bytes_out &= VIA_XSTORE_CNT_MASK; | ||
102 | if (bytes_out == 0) | ||
103 | return 0; | ||
104 | return 1; | ||
105 | } | ||
106 | |||
107 | static int via_rng_data_read(struct hwrng *rng, u32 *data) | ||
108 | { | ||
109 | u32 via_rng_datum = (u32)rng->priv; | ||
110 | |||
111 | *data = via_rng_datum; | ||
112 | |||
113 | return 1; | ||
114 | } | ||
115 | |||
116 | static int via_rng_init(struct hwrng *rng) | ||
117 | { | ||
118 | u32 lo, hi, old_lo; | ||
119 | |||
120 | /* Control the RNG via MSR. Tread lightly and pay very close | ||
121 | * close attention to values written, as the reserved fields | ||
122 | * are documented to be "undefined and unpredictable"; but it | ||
123 | * does not say to write them as zero, so I make a guess that | ||
124 | * we restore the values we find in the register. | ||
125 | */ | ||
126 | rdmsr(MSR_VIA_RNG, lo, hi); | ||
127 | |||
128 | old_lo = lo; | ||
129 | lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT); | ||
130 | lo &= ~VIA_XSTORE_CNT_MASK; | ||
131 | lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE); | ||
132 | lo |= VIA_RNG_ENABLE; | ||
133 | |||
134 | if (lo != old_lo) | ||
135 | wrmsr(MSR_VIA_RNG, lo, hi); | ||
136 | |||
137 | /* perhaps-unnecessary sanity check; remove after testing if | ||
138 | unneeded */ | ||
139 | rdmsr(MSR_VIA_RNG, lo, hi); | ||
140 | if ((lo & VIA_RNG_ENABLE) == 0) { | ||
141 | printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n"); | ||
142 | return -ENODEV; | ||
143 | } | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | |||
149 | static struct hwrng via_rng = { | ||
150 | .name = "via", | ||
151 | .init = via_rng_init, | ||
152 | .data_present = via_rng_data_present, | ||
153 | .data_read = via_rng_data_read, | ||
154 | }; | ||
155 | |||
156 | |||
157 | static int __init mod_init(void) | ||
158 | { | ||
159 | int err; | ||
160 | |||
161 | if (!cpu_has_xstore) | ||
162 | return -ENODEV; | ||
163 | printk(KERN_INFO "VIA RNG detected\n"); | ||
164 | err = hwrng_register(&via_rng); | ||
165 | if (err) { | ||
166 | printk(KERN_ERR PFX "RNG registering failed (%d)\n", | ||
167 | err); | ||
168 | goto out; | ||
169 | } | ||
170 | out: | ||
171 | return err; | ||
172 | } | ||
173 | |||
174 | static void __exit mod_exit(void) | ||
175 | { | ||
176 | hwrng_unregister(&via_rng); | ||
177 | } | ||
178 | |||
179 | subsys_initcall(mod_init); | ||
180 | module_exit(mod_exit); | ||
181 | |||
182 | MODULE_DESCRIPTION("H/W RNG driver for VIA chipsets"); | ||
183 | MODULE_LICENSE("GPL"); | ||