patch-2.4.0-test9 linux/drivers/char/i810_rng.c
Next file: linux/drivers/char/joystick/pcigame.c
Previous file: linux/drivers/char/i810-tco.c
Back to the patch index
Back to the overall index
- Lines: 442
- Date:
Mon Oct 2 12:00:16 2000
- Orig file:
v2.4.0-test8/linux/drivers/char/i810_rng.c
- Orig date:
Wed Jul 12 21:58:42 2000
diff -u --recursive --new-file v2.4.0-test8/linux/drivers/char/i810_rng.c linux/drivers/char/i810_rng.c
@@ -119,13 +119,6 @@
* The timer and the character device may be used simultaneously,
if desired.
- * FIXME: Currently only one open() of the character device is allowed.
- If another user tries to open() the device, they will get an
- -EBUSY error. Instead, this really should either support
- multiple simultaneous users of the character device (not hard),
- or simply block open() until the current user of the chrdev
- calls close().
-
* FIXME: support poll()
* FIXME: should we be crazy and support mmap()?
@@ -138,11 +131,16 @@
This will slow things down but guarantee that bad data is
never passed upstream.
+ * Since the RNG is accessed from a timer as well as normal
+ kernel code, but not from interrupts, we use spin_lock_bh
+ in regular code, and spin_lock in the timer function, to
+ serialize access to the RNG hardware area.
+
----------------------------------------------------------
Change history:
- 0.6.2:
+ Version 0.6.2:
* Clean up spinlocks. Since we don't have any interrupts
to worry about, but we do have a timer to worry about,
we use spin_lock_bh everywhere except the timer function
@@ -152,6 +150,20 @@
* New timer interval sysctl
* Clean up sysctl names
+ Version 0.9.0:
+ * Don't register a pci_driver, because we are really
+ using PCI bridge vendor/device ids, and someone
+ may want to register a driver for the bridge. (bug fix)
+ * Don't let the usage count go negative (bug fix)
+ * Clean up spinlocks (bug fix)
+ * Enable PCI device, if necessary (bug fix)
+ * iounmap on module unload (bug fix)
+ * If RNG chrdev is already in use when open(2) is called,
+ sleep until it is available.
+ * Remove redundant globals rng_allocated, rng_use_count
+ * Convert numeric globals to unsigned
+ * Module unload cleanup
+
*/
@@ -166,6 +178,7 @@
#include <linux/sysctl.h>
#include <linux/miscdevice.h>
#include <linux/smp_lock.h>
+#include <linux/mm.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -174,7 +187,7 @@
/*
* core module and version information
*/
-#define RNG_VERSION "0.6.2"
+#define RNG_VERSION "0.9.0"
#define RNG_MODULE_NAME "i810_rng"
#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION
#define PFX RNG_MODULE_NAME ": "
@@ -253,22 +266,24 @@
* various RNG status variables. they are globals
* as we only support a single RNG device
*/
-static int rng_allocated; /* is someone using the RNG region? */
static int rng_hw_enabled; /* is the RNG h/w enabled? */
static int rng_timer_enabled; /* is the RNG timer enabled? */
-static int rng_use_count; /* number of times RNG has been enabled */
static int rng_trusted; /* does FIPS trust out data? */
static int rng_enabled_sysctl; /* sysctl for enabling/disabling RNG */
-static int rng_entropy = 8; /* number of entropy bits we submit to /dev/random */
-static int rng_entropy_sysctl; /* sysctl for changing entropy bits */
-static int rng_interval_sysctl; /* sysctl for changing timer interval */
+static unsigned int rng_entropy = 8; /* number of entropy bits we submit to /dev/random */
+static unsigned int rng_entropy_sysctl; /* sysctl for changing entropy bits */
+static unsigned int rng_interval_sysctl; /* sysctl for changing timer interval */
static int rng_have_mem_region; /* did we grab RNG region via request_mem_region? */
-static int rng_fips_counter; /* size of internal FIPS test data pool */
-static int rng_timer_len = RNG_DEF_TIMER_LEN; /* timer interval, in jiffies */
+static unsigned int rng_fips_counter; /* size of internal FIPS test data pool */
+static unsigned int rng_timer_len = RNG_DEF_TIMER_LEN; /* timer interval, in jiffies */
static void *rng_mem; /* token to our ioremap'd RNG register area */
static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; /* hardware lock */
static struct timer_list rng_timer; /* kernel timer for RNG hardware reads and tests */
-static int rng_open; /* boolean, 0 (false) if chrdev is closed, 1 (true) if open */
+static struct pci_dev *rng_pdev; /* Firmware Hub PCI device found during PCI probe */
+static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */
+static wait_queue_head_t rng_open_wait; /* Wait queue for serializing open/release */
+static int rng_open_mode; /* Open mode (we only allow reads) */
+
/*
* inlined helper functions for accessing RNG registers
@@ -320,6 +335,8 @@
/* gimme some thermal noise, baby */
rng_data = rng_data_read ();
+ spin_unlock (&rng_lock);
+
/*
* if RNG has been verified in the past, add
* data just read to the /dev/random pool,
@@ -333,6 +350,8 @@
rng_fips_test_store (rng_data);
if (rng_fips_counter > RNG_FIPS_TEST_THRESHOLD)
rng_run_fips_test ();
+ } else {
+ spin_unlock (&rng_lock);
}
/* run the timer again, if enabled */
@@ -340,9 +359,6 @@
rng_timer.expires = jiffies + rng_timer_len;
add_timer (&rng_timer);
}
-
- spin_unlock (&rng_lock);
-
}
@@ -351,8 +367,8 @@
*/
static int rng_enable (int enable)
{
- int rc = 0;
- u8 hw_status;
+ int rc = 0, action = 0;
+ u8 hw_status, new_status;
DPRINTK ("ENTER\n");
@@ -362,28 +378,36 @@
if (enable) {
rng_hw_enabled = 1;
- rng_use_count++;
MOD_INC_USE_COUNT;
} else {
- rng_use_count--;
- if (rng_use_count == 0)
+#ifndef __alpha__
+ if (GET_USE_COUNT (THIS_MODULE) > 0)
+ MOD_DEC_USE_COUNT;
+ if (GET_USE_COUNT (THIS_MODULE) == 0)
rng_hw_enabled = 0;
- MOD_DEC_USE_COUNT;
+#endif
}
if (rng_hw_enabled && ((hw_status & RNG_ENABLED) == 0)) {
rng_hwstatus_set (hw_status | RNG_ENABLED);
- printk (KERN_INFO PFX "RNG h/w enabled\n");
+ action = 1;
}
else if (!rng_hw_enabled && (hw_status & RNG_ENABLED)) {
rng_hwstatus_set (hw_status & ~RNG_ENABLED);
- printk (KERN_INFO PFX "RNG h/w disabled\n");
+ action = 2;
}
+ new_status = rng_hwstatus ();
+
spin_unlock_bh (&rng_lock);
- if ((!!enable) != (!!(rng_hwstatus () & RNG_ENABLED))) {
+ if (action == 1)
+ printk (KERN_INFO PFX "RNG h/w enabled\n");
+ else if (action == 2)
+ printk (KERN_INFO PFX "RNG h/w disabled\n");
+
+ if ((!!enable) != (!!(new_status & RNG_ENABLED))) {
printk (KERN_ERR PFX "Unable to %sable the RNG\n",
enable ? "en" : "dis");
rc = -EIO;
@@ -406,15 +430,14 @@
DPRINTK ("ENTER\n");
spin_lock_bh (&rng_lock);
-
rng_enabled_sysctl = enabled_save = rng_timer_enabled;
+ spin_unlock_bh (&rng_lock);
rc = proc_dointvec (table, write, filp, buffer, lenp);
- if (rc) {
- spin_unlock_bh (&rng_lock);
+ if (rc)
return rc;
- }
+ spin_lock_bh (&rng_lock);
if (enabled_save != rng_enabled_sysctl) {
rng_timer_enabled = rng_enabled_sysctl;
spin_unlock_bh (&rng_lock);
@@ -591,53 +614,49 @@
int rc = -EINVAL;
if ((filp->f_mode & FMODE_READ) == 0)
- goto err_out;
+ goto err_out_ret;
if (filp->f_mode & FMODE_WRITE)
- goto err_out;
+ goto err_out_ret;
- spin_lock_bh (&rng_lock);
-
- /* only allow one open of this device, exit with -EBUSY if already open */
- /* FIXME: we should sleep on a semaphore here, unless O_NONBLOCK */
- if (rng_open) {
- spin_unlock_bh (&rng_lock);
- rc = -EBUSY;
- goto err_out;
+ /* wait for device to become free */
+ down (&rng_open_sem);
+ while (rng_open_mode & filp->f_mode) {
+ if (filp->f_flags & O_NONBLOCK) {
+ up (&rng_open_sem);
+ return -EWOULDBLOCK;
+ }
+ up (&rng_open_sem);
+ interruptible_sleep_on (&rng_open_wait);
+ if (signal_pending (current))
+ return -ERESTARTSYS;
+ down (&rng_open_sem);
}
- rng_open = 1;
-
- spin_unlock_bh (&rng_lock);
-
- if (rng_enable(1) != 0) {
- spin_lock_bh (&rng_lock);
- rng_open = 0;
- spin_unlock_bh (&rng_lock);
+ if (rng_enable (1)) {
rc = -EIO;
goto err_out;
}
+ rng_open_mode |= filp->f_mode & (FMODE_READ | FMODE_WRITE);
+ up (&rng_open_sem);
return 0;
err_out:
+ up (&rng_open_sem);
+err_out_ret:
return rc;
}
static int rng_dev_release (struct inode *inode, struct file *filp)
{
+ down(&rng_open_sem);
- lock_kernel();
- if (rng_enable(0) != 0) {
- unlock_kernel();
- return -EIO;
- }
-
- spin_lock_bh (&rng_lock);
- rng_open = 0;
- spin_unlock_bh (&rng_lock);
- unlock_kernel();
+ rng_enable(0);
+ rng_open_mode &= (~filp->f_mode) & (FMODE_READ|FMODE_WRITE);
+ up (&rng_open_sem);
+ wake_up (&rng_open_wait);
return 0;
}
@@ -705,19 +724,15 @@
/*
* rng_init_one - look for and attempt to init a single RNG
*/
-static int __init rng_init_one (struct pci_dev *dev,
- const struct pci_device_id *id)
+static int __init rng_init_one (struct pci_dev *dev)
{
int rc;
u8 hw_status;
DPRINTK ("ENTER\n");
- if (rng_allocated) {
- printk (KERN_ERR PFX "this driver only supports one RNG\n");
- DPRINTK ("EXIT, returning -EBUSY\n");
- return -EBUSY;
- }
+ if (pci_enable_device (dev))
+ return -EIO;
/* XXX currently fails, investigate who has our mem region */
if (request_mem_region (RNG_ADDR, RNG_ADDR_LEN, RNG_MODULE_NAME))
@@ -728,7 +743,7 @@
printk (KERN_ERR PFX "cannot ioremap RNG Memory\n");
DPRINTK ("EXIT, returning -EBUSY\n");
rc = -EBUSY;
- goto err_out;
+ goto err_out_free_res;
}
/* Check for Intel 82802 */
@@ -737,11 +752,9 @@
printk (KERN_ERR PFX "RNG not detected\n");
DPRINTK ("EXIT, returning -ENODEV\n");
rc = -ENODEV;
- goto err_out;
+ goto err_out_free_map;
}
- rng_allocated = 1;
-
if (rng_entropy < 0 || rng_entropy > RNG_MAX_ENTROPY)
rng_entropy = RNG_MAX_ENTROPY;
@@ -749,10 +762,11 @@
init_timer (&rng_timer);
rng_timer.function = rng_timer_tick;
+ /* turn RNG h/w off, if it's on */
rc = rng_enable (0);
if (rc) {
printk (KERN_ERR PFX "cannot disable RNG, aborting\n");
- goto err_out;
+ goto err_out_free_map;
}
/* add sysctls */
@@ -761,9 +775,9 @@
DPRINTK ("EXIT, returning 0\n");
return 0;
-err_out:
- if (rng_mem)
- iounmap (rng_mem);
+err_out_free_map:
+ iounmap (rng_mem);
+err_out_free_res:
if (rng_have_mem_region)
release_mem_region (RNG_ADDR, RNG_ADDR_LEN);
return rc;
@@ -772,6 +786,11 @@
/*
* Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE. We do not actually
+ * register a pci_driver, because someone else might one day
+ * want to register another driver on the same PCI id.
*/
const static struct pci_device_id rng_pci_tbl[] __initdata = {
{ 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, },
@@ -780,11 +799,6 @@
};
MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
-static struct pci_driver rng_driver = {
- name: RNG_MODULE_NAME,
- id_table: rng_pci_tbl,
- probe: rng_init_one,
-};
MODULE_AUTHOR("Jeff Garzik, Matt Sottek");
MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver");
@@ -813,23 +827,36 @@
static int __init rng_init (void)
{
int rc;
+ struct pci_dev *pdev;
DPRINTK ("ENTER\n");
- if (pci_register_driver (&rng_driver) < 1) {
- DPRINTK ("EXIT, returning -ENODEV\n");
+ init_MUTEX (&rng_open_sem);
+ init_waitqueue_head (&rng_open_wait);
+
+ pdev = pci_find_device (0x8086, 0x2418, NULL);
+ if (!pdev)
+ pdev = pci_find_device (0x8086, 0x2428, NULL);
+ if (!pdev)
return -ENODEV;
- }
+
+ rc = rng_init_one (pdev);
+ if (rc)
+ return rc;
rc = misc_register (&rng_miscdev);
if (rc) {
- pci_unregister_driver (&rng_driver);
+ iounmap (rng_mem);
+ if (rng_have_mem_region)
+ release_mem_region (RNG_ADDR, RNG_ADDR_LEN);
DPRINTK ("EXIT, returning %d\n", rc);
return rc;
}
printk (KERN_INFO RNG_DRIVER_NAME " loaded\n");
+ rng_pdev = pdev;
+
DPRINTK ("EXIT, returning 0\n");
return 0;
}
@@ -842,17 +869,16 @@
{
DPRINTK ("ENTER\n");
- del_timer_sync (&rng_timer);
+ assert (rng_timer_enabled == 0);
+ assert (rng_hw_enabled == 0);
+
+ misc_deregister (&rng_miscdev);
rng_sysctl (0);
- pci_unregister_driver (&rng_driver);
+ iounmap (rng_mem);
if (rng_have_mem_region)
release_mem_region (RNG_ADDR, RNG_ADDR_LEN);
-
- rng_hwstatus_set (rng_hwstatus() & ~RNG_ENABLED);
-
- misc_deregister (&rng_miscdev);
DPRINTK ("EXIT\n");
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)