1 /*
2
3 Hardware driver for Intel i810 Random Number Generator (RNG)
4 Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
5 Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
6
7 Driver Web site: http://sourceforge.net/projects/gkernel/
8
9 Please read Documentation/i810_rng.txt for details on use.
10
11 ----------------------------------------------------------
12
13 This software may be used and distributed according to the terms
14 of the GNU General Public License, incorporated herein by reference.
15
16 */
17
18
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/fs.h>
22 #include <linux/init.h>
23 #include <linux/pci.h>
24 #include <linux/interrupt.h>
25 #include <linux/spinlock.h>
26 #include <linux/random.h>
27 #include <linux/miscdevice.h>
28 #include <linux/smp_lock.h>
29 #include <linux/mm.h>
30 #include <linux/delay.h>
31
32 #include <asm/io.h>
33 #include <asm/uaccess.h>
34
35
36 /*
37 * core module and version information
38 */
39 #define RNG_VERSION "0.9.8"
40 #define RNG_MODULE_NAME "i810_rng"
41 #define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION
42 #define PFX RNG_MODULE_NAME ": "
43
44
45 /*
46 * debugging macros
47 */
48 #undef RNG_DEBUG /* define to enable copious debugging info */
49
50 #ifdef RNG_DEBUG
51 /* note: prints function name for you */
52 #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
53 #else
54 #define DPRINTK(fmt, args...)
55 #endif
56
57 #undef RNG_NDEBUG /* define to disable lightweight runtime checks */
58 #ifdef RNG_NDEBUG
59 #define assert(expr)
60 #else
61 #define assert(expr) \
62 if(!(expr)) { \
63 printk( "Assertion failed! %s,%s,%s,line=%d\n", \
64 #expr,__FILE__,__FUNCTION__,__LINE__); \
65 }
66 #endif
67
68
69 /*
70 * RNG registers (offsets from rng_mem)
71 */
72 #define RNG_HW_STATUS 0
73 #define RNG_PRESENT 0x40
74 #define RNG_ENABLED 0x01
75 #define RNG_STATUS 1
76 #define RNG_DATA_PRESENT 0x01
77 #define RNG_DATA 2
78
79 /*
80 * Magic address at which Intel PCI bridges locate the RNG
81 */
82 #define RNG_ADDR 0xFFBC015F
83 #define RNG_ADDR_LEN 3
84
85 #define RNG_MISCDEV_MINOR 183 /* official */
86
87 /*
88 * various RNG status variables. they are globals
89 * as we only support a single RNG device
90 */
91 static void *rng_mem; /* token to our ioremap'd RNG register area */
92 static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */
93
94
95 /*
96 * inlined helper functions for accessing RNG registers
97 */
rng_hwstatus(void)98 static inline u8 rng_hwstatus (void)
99 {
100 assert (rng_mem != NULL);
101 return readb (rng_mem + RNG_HW_STATUS);
102 }
103
rng_hwstatus_set(u8 hw_status)104 static inline u8 rng_hwstatus_set (u8 hw_status)
105 {
106 assert (rng_mem != NULL);
107 writeb (hw_status, rng_mem + RNG_HW_STATUS);
108 return rng_hwstatus ();
109 }
110
111
rng_data_present(void)112 static inline int rng_data_present (void)
113 {
114 assert (rng_mem != NULL);
115
116 return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0;
117 }
118
119
rng_data_read(void)120 static inline int rng_data_read (void)
121 {
122 assert (rng_mem != NULL);
123
124 return readb (rng_mem + RNG_DATA);
125 }
126
127 /*
128 * rng_enable - enable the RNG hardware
129 */
130
rng_enable(void)131 static int rng_enable (void)
132 {
133 int rc = 0;
134 u8 hw_status, new_status;
135
136 DPRINTK ("ENTER\n");
137
138 hw_status = rng_hwstatus ();
139
140 if ((hw_status & RNG_ENABLED) == 0) {
141 new_status = rng_hwstatus_set (hw_status | RNG_ENABLED);
142
143 if (new_status & RNG_ENABLED)
144 printk (KERN_INFO PFX "RNG h/w enabled\n");
145 else {
146 printk (KERN_ERR PFX "Unable to enable the RNG\n");
147 rc = -EIO;
148 }
149 }
150
151 DPRINTK ("EXIT, returning %d\n", rc);
152 return rc;
153 }
154
155 /*
156 * rng_disable - disable the RNG hardware
157 */
158
rng_disable(void)159 static void rng_disable(void)
160 {
161 u8 hw_status, new_status;
162
163 DPRINTK ("ENTER\n");
164
165 hw_status = rng_hwstatus ();
166
167 if (hw_status & RNG_ENABLED) {
168 new_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED);
169
170 if ((new_status & RNG_ENABLED) == 0)
171 printk (KERN_INFO PFX "RNG h/w disabled\n");
172 else {
173 printk (KERN_ERR PFX "Unable to disable the RNG\n");
174 }
175 }
176
177 DPRINTK ("EXIT\n");
178 }
179
rng_dev_open(struct inode * inode,struct file * filp)180 static int rng_dev_open (struct inode *inode, struct file *filp)
181 {
182 int rc;
183
184 if ((filp->f_mode & FMODE_READ) == 0)
185 return -EINVAL;
186 if (filp->f_mode & FMODE_WRITE)
187 return -EINVAL;
188
189 /* wait for device to become free */
190 if (filp->f_flags & O_NONBLOCK) {
191 if (down_trylock (&rng_open_sem))
192 return -EAGAIN;
193 } else {
194 if (down_interruptible (&rng_open_sem))
195 return -ERESTARTSYS;
196 }
197
198 rc = rng_enable ();
199 if (rc) {
200 up (&rng_open_sem);
201 return rc;
202 }
203
204 return 0;
205 }
206
207
rng_dev_release(struct inode * inode,struct file * filp)208 static int rng_dev_release (struct inode *inode, struct file *filp)
209 {
210 rng_disable ();
211 up (&rng_open_sem);
212 return 0;
213 }
214
215
rng_dev_read(struct file * filp,char * buf,size_t size,loff_t * offp)216 static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size,
217 loff_t * offp)
218 {
219 static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED;
220 int have_data;
221 u8 data = 0;
222 ssize_t ret = 0;
223
224 while (size) {
225 spin_lock (&rng_lock);
226
227 have_data = 0;
228 if (rng_data_present ()) {
229 data = rng_data_read ();
230 have_data = 1;
231 }
232
233 spin_unlock (&rng_lock);
234
235 if (have_data) {
236 if (put_user (data, buf++)) {
237 ret = ret ? : -EFAULT;
238 break;
239 }
240 size--;
241 ret++;
242 }
243
244 if (filp->f_flags & O_NONBLOCK)
245 return ret ? : -EAGAIN;
246
247 if (current->need_resched)
248 {
249 current->state = TASK_INTERRUPTIBLE;
250 schedule_timeout(1);
251 }
252 else
253 udelay(200);
254
255 if (signal_pending (current))
256 return ret ? : -ERESTARTSYS;
257 }
258
259 return ret;
260 }
261
262
263 static struct file_operations rng_chrdev_ops = {
264 owner: THIS_MODULE,
265 open: rng_dev_open,
266 release: rng_dev_release,
267 read: rng_dev_read,
268 };
269
270
271 static struct miscdevice rng_miscdev = {
272 RNG_MISCDEV_MINOR,
273 RNG_MODULE_NAME,
274 &rng_chrdev_ops,
275 };
276
277
278 /*
279 * rng_init_one - look for and attempt to init a single RNG
280 */
rng_init_one(struct pci_dev * dev)281 static int __init rng_init_one (struct pci_dev *dev)
282 {
283 int rc;
284 u8 hw_status;
285
286 DPRINTK ("ENTER\n");
287
288 rc = misc_register (&rng_miscdev);
289 if (rc) {
290 printk (KERN_ERR PFX "cannot register misc device\n");
291 DPRINTK ("EXIT, returning %d\n", rc);
292 goto err_out;
293 }
294
295 rng_mem = ioremap (RNG_ADDR, RNG_ADDR_LEN);
296 if (rng_mem == NULL) {
297 printk (KERN_ERR PFX "cannot ioremap RNG Memory\n");
298 DPRINTK ("EXIT, returning -EBUSY\n");
299 rc = -EBUSY;
300 goto err_out_free_miscdev;
301 }
302
303 /* Check for Intel 82802 */
304 hw_status = rng_hwstatus ();
305 if ((hw_status & RNG_PRESENT) == 0) {
306 printk (KERN_ERR PFX "RNG not detected\n");
307 DPRINTK ("EXIT, returning -ENODEV\n");
308 rc = -ENODEV;
309 goto err_out_free_map;
310 }
311
312 /* turn RNG h/w off, if it's on */
313 if (hw_status & RNG_ENABLED)
314 hw_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED);
315 if (hw_status & RNG_ENABLED) {
316 printk (KERN_ERR PFX "cannot disable RNG, aborting\n");
317 goto err_out_free_map;
318 }
319
320 DPRINTK ("EXIT, returning 0\n");
321 return 0;
322
323 err_out_free_map:
324 iounmap (rng_mem);
325 err_out_free_miscdev:
326 misc_deregister (&rng_miscdev);
327 err_out:
328 return rc;
329 }
330
331
332 /*
333 * Data for PCI driver interface
334 *
335 * This data only exists for exporting the supported
336 * PCI ids via MODULE_DEVICE_TABLE. We do not actually
337 * register a pci_driver, because someone else might one day
338 * want to register another driver on the same PCI id.
339 */
340 static struct pci_device_id rng_pci_tbl[] __initdata = {
341 { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, },
342 { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, },
343 { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, },
344 { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, },
345 { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, },
346 { 0, },
347 };
348 MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
349
350
351 MODULE_AUTHOR("Jeff Garzik, Philipp Rumpf, Matt Sottek");
352 MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver");
353 MODULE_LICENSE("GPL");
354
355
356 /*
357 * rng_init - initialize RNG module
358 */
rng_init(void)359 static int __init rng_init (void)
360 {
361 int rc;
362 struct pci_dev *pdev;
363
364 DPRINTK ("ENTER\n");
365
366 init_MUTEX (&rng_open_sem);
367
368 pci_for_each_dev(pdev) {
369 if (pci_match_device (rng_pci_tbl, pdev) != NULL)
370 goto match;
371 }
372
373 DPRINTK ("EXIT, returning -ENODEV\n");
374 return -ENODEV;
375
376 match:
377 rc = rng_init_one (pdev);
378 if (rc)
379 return rc;
380
381 printk (KERN_INFO RNG_DRIVER_NAME " loaded\n");
382
383 DPRINTK ("EXIT, returning 0\n");
384 return 0;
385 }
386
387
388 /*
389 * rng_init - shutdown RNG module
390 */
rng_cleanup(void)391 static void __exit rng_cleanup (void)
392 {
393 DPRINTK ("ENTER\n");
394
395 misc_deregister (&rng_miscdev);
396
397 iounmap (rng_mem);
398
399 DPRINTK ("EXIT\n");
400 }
401
402
403 module_init (rng_init);
404 module_exit (rng_cleanup);
405