diff options
Diffstat (limited to 'drivers/char/hw_random/intel-rng.c')
| -rw-r--r-- | drivers/char/hw_random/intel-rng.c | 219 | 
1 files changed, 122 insertions, 97 deletions
diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c index cc1046e6ee0..4ae9811d1a6 100644 --- a/drivers/char/hw_random/intel-rng.c +++ b/drivers/char/hw_random/intel-rng.c @@ -24,10 +24,11 @@   * warranty of any kind, whether express or implied.   */ -#include <linux/module.h> +#include <linux/hw_random.h>  #include <linux/kernel.h> +#include <linux/module.h>  #include <linux/pci.h> -#include <linux/hw_random.h> +#include <linux/stop_machine.h>  #include <asm/io.h> @@ -217,30 +218,117 @@ static struct hwrng intel_rng = {  	.data_read	= intel_rng_data_read,  }; +struct intel_rng_hw { +	struct pci_dev *dev; +	void __iomem *mem; +	u8 bios_cntl_off; +	u8 bios_cntl_val; +	u8 fwh_dec_en1_off; +	u8 fwh_dec_en1_val; +}; -#ifdef CONFIG_SMP -static char __initdata waitflag; +static int __init intel_rng_hw_init(void *_intel_rng_hw) +{ +	struct intel_rng_hw *intel_rng_hw = _intel_rng_hw; +	u8 mfc, dvc; + +	/* interrupts disabled in stop_machine_run call */ + +	if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK)) +		pci_write_config_byte(intel_rng_hw->dev, +		                      intel_rng_hw->fwh_dec_en1_off, +		                      intel_rng_hw->fwh_dec_en1_val | +				      FWH_F8_EN_MASK); +	if (!(intel_rng_hw->bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK)) +		pci_write_config_byte(intel_rng_hw->dev, +		                      intel_rng_hw->bios_cntl_off, +		                      intel_rng_hw->bios_cntl_val | +				      BIOS_CNTL_WRITE_ENABLE_MASK); + +	writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem); +	writeb(INTEL_FWH_READ_ID_CMD, intel_rng_hw->mem); +	mfc = readb(intel_rng_hw->mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS); +	dvc = readb(intel_rng_hw->mem + INTEL_FWH_DEVICE_CODE_ADDRESS); +	writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem); + +	if (!(intel_rng_hw->bios_cntl_val & +	      (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))) +		pci_write_config_byte(intel_rng_hw->dev, +				      intel_rng_hw->bios_cntl_off, +				      intel_rng_hw->bios_cntl_val); +	if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK)) +		pci_write_config_byte(intel_rng_hw->dev, +				      intel_rng_hw->fwh_dec_en1_off, +				      intel_rng_hw->fwh_dec_en1_val); -static void __init intel_init_wait(void *unused) +	if (mfc != INTEL_FWH_MANUFACTURER_CODE || +	    (dvc != INTEL_FWH_DEVICE_CODE_8M && +	     dvc != INTEL_FWH_DEVICE_CODE_4M)) { +		printk(KERN_ERR PFX "FWH not detected\n"); +		return -ENODEV; +	} + +	return 0; +} + +static int __init intel_init_hw_struct(struct intel_rng_hw *intel_rng_hw, +					struct pci_dev *dev)  { -	while (waitflag) -		cpu_relax(); +	intel_rng_hw->bios_cntl_val = 0xff; +	intel_rng_hw->fwh_dec_en1_val = 0xff; +	intel_rng_hw->dev = dev; + +	/* Check for Intel 82802 */ +	if (dev->device < 0x2640) { +		intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD; +		intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_OLD; +	} else { +		intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW; +		intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_NEW; +	} + +	pci_read_config_byte(dev, intel_rng_hw->fwh_dec_en1_off, +			     &intel_rng_hw->fwh_dec_en1_val); +	pci_read_config_byte(dev, intel_rng_hw->bios_cntl_off, +			     &intel_rng_hw->bios_cntl_val); + +	if ((intel_rng_hw->bios_cntl_val & +	     (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)) +	    == BIOS_CNTL_LOCK_ENABLE_MASK) { +		static __initdata /*const*/ char warning[] = +			KERN_WARNING PFX "Firmware space is locked read-only. " +			KERN_WARNING PFX "If you can't or\n don't want to " +			KERN_WARNING PFX "disable this in firmware setup, and " +			KERN_WARNING PFX "if\n you are certain that your " +			KERN_WARNING PFX "system has a functional\n RNG, try" +			KERN_WARNING PFX "using the 'no_fwh_detect' option.\n"; + +		if (no_fwh_detect) +			return -ENODEV; +		printk(warning); +		return -EBUSY; +	} + +	intel_rng_hw->mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN); +	if (intel_rng_hw->mem == NULL) +		return -EBUSY; + +	return 0;  } -#endif +  static int __init mod_init(void)  {  	int err = -ENODEV; -	unsigned i; +	int i;  	struct pci_dev *dev = NULL; -	void __iomem *mem; -	unsigned long flags; -	u8 bios_cntl_off, fwh_dec_en1_off; -	u8 bios_cntl_val = 0xff, fwh_dec_en1_val = 0xff; -	u8 hw_status, mfc, dvc; +	void __iomem *mem = mem; +	u8 hw_status; +	struct intel_rng_hw *intel_rng_hw;  	for (i = 0; !dev && pci_tbl[i].vendor; ++i) -		dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device, NULL); +		dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device, +				     NULL);  	if (!dev)  		goto out; /* Device not found. */ @@ -250,39 +338,18 @@ static int __init mod_init(void)  		goto fwh_done;  	} -	/* Check for Intel 82802 */ -	if (dev->device < 0x2640) { -		fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD; -		bios_cntl_off = BIOS_CNTL_REG_OLD; -	} else { -		fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW; -		bios_cntl_off = BIOS_CNTL_REG_NEW; -	} - -	pci_read_config_byte(dev, fwh_dec_en1_off, &fwh_dec_en1_val); -	pci_read_config_byte(dev, bios_cntl_off, &bios_cntl_val); - -	if ((bios_cntl_val & -	     (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)) -	    == BIOS_CNTL_LOCK_ENABLE_MASK) { -		static __initdata /*const*/ char warning[] = -			KERN_WARNING PFX "Firmware space is locked read-only. If you can't or\n" -			KERN_WARNING PFX "don't want to disable this in firmware setup, and if\n" -			KERN_WARNING PFX "you are certain that your system has a functional\n" -			KERN_WARNING PFX "RNG, try using the 'no_fwh_detect' option.\n"; - +	intel_rng_hw = kmalloc(sizeof(*intel_rng_hw), GFP_KERNEL); +	if (!intel_rng_hw) {  		pci_dev_put(dev); -		if (no_fwh_detect) -			goto fwh_done; -		printk(warning); -		err = -EBUSY;  		goto out;  	} -	mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN); -	if (mem == NULL) { +	err = intel_init_hw_struct(intel_rng_hw, dev); +	if (err) {  		pci_dev_put(dev); -		err = -EBUSY; +		kfree(intel_rng_hw); +		if (err == -ENODEV) +			goto fwh_done;  		goto out;  	} @@ -290,59 +357,18 @@ static int __init mod_init(void)  	 * Since the BIOS code/data is going to disappear from its normal  	 * location with the Read ID command, all activity on the system  	 * must be stopped until the state is back to normal. +	 * +	 * Use stop_machine_run because IPIs can be blocked by disabling +	 * interrupts.  	 */ -#ifdef CONFIG_SMP -	set_mb(waitflag, 1); -	if (smp_call_function(intel_init_wait, NULL, 1, 0) != 0) { -		set_mb(waitflag, 0); -		pci_dev_put(dev); -		printk(KERN_ERR PFX "cannot run on all processors\n"); -		err = -EAGAIN; -		goto err_unmap; -	} -#endif -	local_irq_save(flags); - -	if (!(fwh_dec_en1_val & FWH_F8_EN_MASK)) -		pci_write_config_byte(dev, -		                      fwh_dec_en1_off, -		                      fwh_dec_en1_val | FWH_F8_EN_MASK); -	if (!(bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK)) -		pci_write_config_byte(dev, -		                      bios_cntl_off, -		                      bios_cntl_val | BIOS_CNTL_WRITE_ENABLE_MASK); - -	writeb(INTEL_FWH_RESET_CMD, mem); -	writeb(INTEL_FWH_READ_ID_CMD, mem); -	mfc = readb(mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS); -	dvc = readb(mem + INTEL_FWH_DEVICE_CODE_ADDRESS); -	writeb(INTEL_FWH_RESET_CMD, mem); - -	if (!(bios_cntl_val & -	      (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))) -		pci_write_config_byte(dev, bios_cntl_off, bios_cntl_val); -	if (!(fwh_dec_en1_val & FWH_F8_EN_MASK)) -		pci_write_config_byte(dev, fwh_dec_en1_off, fwh_dec_en1_val); - -	local_irq_restore(flags); -#ifdef CONFIG_SMP -	/* Tell other CPUs to resume. */ -	set_mb(waitflag, 0); -#endif - -	iounmap(mem); +	err = stop_machine_run(intel_rng_hw_init, intel_rng_hw, NR_CPUS);  	pci_dev_put(dev); - -	if (mfc != INTEL_FWH_MANUFACTURER_CODE || -	    (dvc != INTEL_FWH_DEVICE_CODE_8M && -	     dvc != INTEL_FWH_DEVICE_CODE_4M)) { -		printk(KERN_ERR PFX "FWH not detected\n"); -		err = -ENODEV; +	iounmap(intel_rng_hw->mem); +	kfree(intel_rng_hw); +	if (err)  		goto out; -	}  fwh_done: -  	err = -ENOMEM;  	mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);  	if (!mem) @@ -352,22 +378,21 @@ fwh_done:  	/* Check for Random Number Generator */  	err = -ENODEV;  	hw_status = hwstatus_get(mem); -	if ((hw_status & INTEL_RNG_PRESENT) == 0) -		goto err_unmap; +	if ((hw_status & INTEL_RNG_PRESENT) == 0) { +		iounmap(mem); +		goto out; +	}  	printk(KERN_INFO "Intel 82802 RNG detected\n");  	err = hwrng_register(&intel_rng);  	if (err) {  		printk(KERN_ERR PFX "RNG registering failed (%d)\n",  		       err); -		goto err_unmap; +		iounmap(mem);  	}  out:  	return err; -err_unmap: -	iounmap(mem); -	goto out;  }  static void __exit mod_exit(void)  | 
