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