/* Intel 82802 Firmware HUB Random Number Generator Driver Copyright (c) 2000 Matt Sottek msottek@quiknet.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define __KERNEL__ #define MODULE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Linux 2.2 compatibility */ #ifndef DECLARE_WAITQUEUE #define DECLARE_WAITQUEUE(WAIT, PTR) struct wait_queue WAIT = { PTR, NULL } #endif #ifndef DECLARE_WAIT_QUEUE_HEAD #define DECLARE_WAIT_QUEUE_HEAD(WAIT) struct wait_queue *WAIT #endif #define i82802_ADDRESS 0xffbc015f #define i82802_PRESENT_MASK 0x40 #define i82802_ENABLED_MASK 0x01 #define i82802_STATUS_MASK 0x01 #define RNG_MINOR 0 #define RNG_MAJOR 0 void rng_fips_test(); unsigned char *rng_mem; unsigned int rng_minor=0; unsigned int rng_major=0; int poker[16],runs[12]; long int pokertest; int longrun = 0; int ones = 0; int rng_status = 0; struct timer_list rng_timer; int currentr = 0; int rlength = 0; int laps = 0; int stat_message=0; MODULE_PARM(rng_minor,"i"); MODULE_PARM(rng_major,"i"); void disable_rng() { unsigned long flags; save_flags(flags); cli(); if(rng_status) { writeb(0x40,rng_mem); rng_status = 0; restore_flags(flags); MOD_DEC_USE_COUNT; printk("<1> RNG Disabled\n"); } else { restore_flags(flags); } } void enable_rng() { unsigned long flags; save_flags(flags); cli(); if(!rng_status) { writeb(0x41,rng_mem); rng_status = 1; restore_flags(flags); MOD_INC_USE_COUNT; printk("<1> RNG Enabled\n"); rng_fips_test(); } else { restore_flags(flags); } } void rng_wakeup(u_long rng_wait_queue_headP) { wake_up_interruptible((wait_queue_head_t *)rng_wait_queue_headP); } /* These are the startup tests suggested by the FIPS 140-1 spec section * 4.11.1 (http://csrc.nist.gov/fips/fips1401.htm) * The Monobit, Poker, Runs, and Long Runs tests are implemented below. * This test is run at startup and at periodic intervals to verify * data is sufficently random. If the tests are failed the RNG module * will no longer submit data to the entropy pool, but the tests will * continue to run at the given interval. If at a later time the RNG * passes all tests it will be re-enabled for the next period. * The reason for this is that it is not unlikely that at some time * during normal operation one of the tests will fail. This does not * necessarily mean the RNG is not operating properly, it is just a * statistically rare event. In that case we don't want to forever * disable the RNG, we will just leave it disabled for the period of * time until the tests are rerun. * * For argument sake I tested /proc/urandom with these tests and it * took 142,095 tries before I got a failure, and urandom isn't as * random as random :) */ void rng_fips_test() { int j; unsigned char rbyte = 0; unsigned long flags=0; int passed=1; if(!laps) { /* Initialize */ currentr = 0; rlength = 0; for(j=0; j<16; j++) { poker[j] = 0; } for(j=0; j<12; j++) { runs[j] = 0; } rlength = 999; ones = 0; } if(laps < 2500 ) { while(1) { save_flags(flags); cli(); rbyte = readb(rng_mem); // Check if we're enabled and status is ready if(!(rbyte & i82802_ENABLED_MASK)) { /* Someone turned us off mid test we'll just reset and exit... no need to finish this run */ laps = 0; restore_flags(flags); return; } rbyte = readb(rng_mem + 1); if(rbyte & i82802_STATUS_MASK) { rbyte = readb(rng_mem + 2); restore_flags(flags); break; } restore_flags(flags); mdelay(4); } poker[rbyte>>4] += 1; poker[rbyte & 15] += 1; /* Trick to make sure currentr != the first bit so we don't screw up the first runlength */ if(rlength == 999) { currentr = !((rbyte & 128)>>7); runs[currentr * 6] = -1; rlength = 1; } for(j=7; j>=0; j--) { if(rbyte & 1<>j) == currentr) { rlength++; } else { /* If runlength is 1-6 count it in correct bucket. 0's go in runs[0-5] 1's go in runs[6-11] hence the 6*currentr below */ if(rlength < 6) { runs[rlength - 1 + (6*currentr)]++; } if(rlength >= 6) { runs[5 + (6*currentr)]++; } /* Check if we just failed longrun test */ if(rlength >= longrun) { longrun = rlength; } rlength=1; /* flip the current run type */ currentr = (rbyte & 1<>j; } } laps++; } else { /* add in the last (possibly incomplete) run */ if(rlength <= 6) { runs[rlength - 1 + (6*currentr)]++; } if(rlength == 34) { longrun = 1; } /* Do poker test */ pokertest = 0; for(j=0; j<16; j++) { pokertest += poker[j] * poker[j]; } stat_message = 0; if(! ((ones < 10346) && (ones > 9654)) ){ printk("<1> RNG failed Monobit test...disabling\n"); stat_message = stat_message & 1; passed = 0; } if(! ((pokertest < 1580457) && (pokertest > 1562821)) ){ printk("<1> RNG failed Poker test...disabling\n"); stat_message = stat_message & 2; passed = 0; } if(! ((runs[0] >= 2267) && (runs[0] <= 2733) && (runs[1] >= 1079) && (runs[1] <= 1421) && (runs[2] >= 502) && (runs[2] <= 748) && (runs[3] >= 223) && (runs[3] <= 402) && (runs[4] >= 90) && (runs[4] <= 223) && (runs[5] >= 90) && (runs[5] <= 223) && (runs[6] >= 2267) && (runs[6] <= 2733) && (runs[7] >= 1079) && (runs[7] <= 1421) && (runs[8] >= 502) && (runs[8] <= 748) && (runs[9] >= 223) && (runs[9] <= 402) && (runs[10] >= 90) && (runs[10] <= 223) && (runs[11] >= 90) && (runs[11] <= 223)) ) { printk("<1> RNG failed Runs test...disabling\n"); stat_message = stat_message & 4; passed = 0; } if(longrun >= 34) { printk("<1> RNG failed LongRun test...disabling\n"); stat_message = stat_message & 8; passed = 0; } if(passed){ enable_rng(); } else { disable_rng(); } laps = 0; } init_timer(&rng_timer); rng_timer.expires = jiffies + 1; rng_timer.function = (void *)rng_fips_test; rng_timer.data = (unsigned long)NULL; add_timer(&rng_timer); return; /* * Put rbyte in the entropy pool here! */ } /* * Called when reading from the device. This should go away * if we can put data in the pool directly */ ssize_t rng_read(struct file *rng_file,char *buffer,size_t length, loff_t *off) { unsigned char rbyte; int sleep_time=1; struct timer_list rng_stall_timer; DECLARE_WAITQUEUE(rng_wait_queue, current); DECLARE_WAIT_QUEUE_HEAD(rng_wait_queue_head); while(1) { rbyte = readb(rng_mem); // Check if we're enabled and status is ready if(!(rbyte & i82802_ENABLED_MASK)) { /* The RNG isn't on. We're going to sleep a long time */ sleep_time = 100; } else { rbyte = readb(rng_mem + 1); if(rbyte & i82802_STATUS_MASK) { rbyte = readb(rng_mem + 2); break; } sleep_time = 1; } init_timer(&rng_stall_timer); rng_stall_timer.expires = jiffies + sleep_time; rng_stall_timer.function = (void *)rng_wakeup; rng_stall_timer.data = (unsigned long)&rng_wait_queue_head; add_timer(&rng_stall_timer); interruptible_sleep_on(&rng_wait_queue_head); if(signal_pending(current)) return -ERESTARTSYS; } copy_to_user(buffer,&rbyte,1); rng_file->f_pos++; return 1; } /* Open Device file */ int rng_open(struct inode *rng_inode,struct file *rng_file) { printk("<1> RNG Device opened\n"); MOD_INC_USE_COUNT; return 0; } /* Close Device File */ int rng_release(struct inode *rng_inode, struct file *rng_file) { printk("<1> RNG Device closed\n"); MOD_DEC_USE_COUNT; return 0; } struct file_operations rng_fops = { open:rng_open, release:rng_release, read:rng_read }; int rng_proc_write(struct file *file, const char *buf, unsigned long count, void *data) { if(count >= 1) { if(buf[0] == 48){disable_rng();} if(buf[0] == 49) { enable_rng(); } } return 1; } int rng_proc_read(char *buf, char **start, off_t offset, int len, int unused) { len = sprintf(buf, "Intel 82802 Random Number generator present.\n"); if(rng_status == 1) { len += sprintf(buf + len, " Status: Enabled\n\n"); } else { len += sprintf(buf + len, " Status: Disabled\n\n"); } len += sprintf(buf + len, "Last statistical analysis results:\n"); if(stat_message & 1) {len += sprintf(buf + len, "Failed Ones Test\n");} else {len += sprintf(buf + len, "Passed Ones Test\n");} if(stat_message & 2) {len += sprintf(buf + len, "Failed Poker Test\n");} else { len += sprintf(buf + len, "Passed Poker Test\n"); } if(stat_message & 4) { len += sprintf(buf + len, "Failed Runs Test\n");} else { len += sprintf(buf + len, "Passed Runs Test\n"); } if(stat_message & 8) {len += sprintf(buf + len, "Failed Longrun Test\n");} else { len += sprintf(buf + len, "Passed Longrun Test\n"); } return len; } /* struct proc_dir_entry rng_proc_entry = { 0, 11,"sys/dev/rng", S_IFREG | S_IRUGO, 1,0,0, 0, NULL, (void *)&rng_proc_read, }; */ struct proc_dir_entry *rng_proc_entry; struct proc_dir_entry proc_root; /* Initialize Module: * RNG is detected and enabled if present. The fips 140-1 test is * then run to make sure device is operating properly. Since this * test takes a couple seconds it is scheduled for later and init_module * returns. */ int init_module(void) { int err; unsigned char input; unsigned char bus,function; /* Check for the 810 ICH first */ if(pcibios_find_device(0x8086,0x2418,0,&bus,&function) && pcibios_find_device(0x8086,0x2428,0,&bus,&function)) { printk("<1>RNG not found\n"); return -EBUSY; } else { printk("<3>RNG found an 810 system\n"); } /* Check for Intel 82802 */ rng_mem = ioremap(i82802_ADDRESS,3); if(!rng_mem) { printk("<1>RNG Memory not mapped!\n"); return -EBUSY; } input = readb(rng_mem); if(input & i82802_PRESENT_MASK) { printk("<1>i82802 RNG Detected\n"); enable_rng(); } else { printk("<1>RNG not found\n"); return -EBUSY; } /* Allocate /proc entry */ rng_proc_entry = create_proc_entry("sys/dev/rng", S_IFREG|S_IRUGO, NULL); rng_proc_entry->get_info = (void *)rng_proc_read; rng_proc_entry->write_proc = (void *) rng_proc_write; /* Code for allocating major/minor number */ if(!rng_minor){rng_minor = RNG_MINOR;} if(!rng_major){rng_major = RNG_MAJOR;} err = register_chrdev(rng_major,"rng",&rng_fops); if(err > 0){rng_major = err;} else {return err;} printk("<1>Intel 82802 RNG Module loaded.\n"); return 0; } void cleanup_module(void){ /* Release major/minor */ unregister_chrdev(rng_major,"rng"); /* Release proc entry */ remove_proc_entry("sys/dev/rng",NULL); /* unmap io memory */ iounmap(rng_mem); printk("<1> RNG Module has exited\n"); }