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