aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/hw_random/intel-rng.c219
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 cc1046e6ee02..4ae9811d1a6c 100644
--- a/drivers/char/hw_random/intel-rng.c
+++ b/drivers/char/hw_random/intel-rng.c
@@ -24,10 +24,11 @@
24 * warranty of any kind, whether express or implied. 24 * warranty of any kind, whether express or implied.
25 */ 25 */
26 26
27#include <linux/module.h> 27#include <linux/hw_random.h>
28#include <linux/kernel.h> 28#include <linux/kernel.h>
29#include <linux/module.h>
29#include <linux/pci.h> 30#include <linux/pci.h>
30#include <linux/hw_random.h> 31#include <linux/stop_machine.h>
31#include <asm/io.h> 32#include <asm/io.h>
32 33
33 34
@@ -217,30 +218,117 @@ static struct hwrng intel_rng = {
217 .data_read = intel_rng_data_read, 218 .data_read = intel_rng_data_read,
218}; 219};
219 220
221struct intel_rng_hw {
222 struct pci_dev *dev;
223 void __iomem *mem;
224 u8 bios_cntl_off;
225 u8 bios_cntl_val;
226 u8 fwh_dec_en1_off;
227 u8 fwh_dec_en1_val;
228};
220 229
221#ifdef CONFIG_SMP 230static int __init intel_rng_hw_init(void *_intel_rng_hw)
222static char __initdata waitflag; 231{
232 struct intel_rng_hw *intel_rng_hw = _intel_rng_hw;
233 u8 mfc, dvc;
234
235 /* interrupts disabled in stop_machine_run call */
236
237 if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))
238 pci_write_config_byte(intel_rng_hw->dev,
239 intel_rng_hw->fwh_dec_en1_off,
240 intel_rng_hw->fwh_dec_en1_val |
241 FWH_F8_EN_MASK);
242 if (!(intel_rng_hw->bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK))
243 pci_write_config_byte(intel_rng_hw->dev,
244 intel_rng_hw->bios_cntl_off,
245 intel_rng_hw->bios_cntl_val |
246 BIOS_CNTL_WRITE_ENABLE_MASK);
247
248 writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);
249 writeb(INTEL_FWH_READ_ID_CMD, intel_rng_hw->mem);
250 mfc = readb(intel_rng_hw->mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);
251 dvc = readb(intel_rng_hw->mem + INTEL_FWH_DEVICE_CODE_ADDRESS);
252 writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);
253
254 if (!(intel_rng_hw->bios_cntl_val &
255 (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
256 pci_write_config_byte(intel_rng_hw->dev,
257 intel_rng_hw->bios_cntl_off,
258 intel_rng_hw->bios_cntl_val);
259 if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))
260 pci_write_config_byte(intel_rng_hw->dev,
261 intel_rng_hw->fwh_dec_en1_off,
262 intel_rng_hw->fwh_dec_en1_val);
223 263
224static void __init intel_init_wait(void *unused) 264 if (mfc != INTEL_FWH_MANUFACTURER_CODE ||
265 (dvc != INTEL_FWH_DEVICE_CODE_8M &&
266 dvc != INTEL_FWH_DEVICE_CODE_4M)) {
267 printk(KERN_ERR PFX "FWH not detected\n");
268 return -ENODEV;
269 }
270
271 return 0;
272}
273
274static int __init intel_init_hw_struct(struct intel_rng_hw *intel_rng_hw,
275 struct pci_dev *dev)
225{ 276{
226 while (waitflag) 277 intel_rng_hw->bios_cntl_val = 0xff;
227 cpu_relax(); 278 intel_rng_hw->fwh_dec_en1_val = 0xff;
279 intel_rng_hw->dev = dev;
280
281 /* Check for Intel 82802 */
282 if (dev->device < 0x2640) {
283 intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;
284 intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_OLD;
285 } else {
286 intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;
287 intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_NEW;
288 }
289
290 pci_read_config_byte(dev, intel_rng_hw->fwh_dec_en1_off,
291 &intel_rng_hw->fwh_dec_en1_val);
292 pci_read_config_byte(dev, intel_rng_hw->bios_cntl_off,
293 &intel_rng_hw->bios_cntl_val);
294
295 if ((intel_rng_hw->bios_cntl_val &
296 (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))
297 == BIOS_CNTL_LOCK_ENABLE_MASK) {
298 static __initdata /*const*/ char warning[] =
299 KERN_WARNING PFX "Firmware space is locked read-only. "
300 KERN_WARNING PFX "If you can't or\n don't want to "
301 KERN_WARNING PFX "disable this in firmware setup, and "
302 KERN_WARNING PFX "if\n you are certain that your "
303 KERN_WARNING PFX "system has a functional\n RNG, try"
304 KERN_WARNING PFX "using the 'no_fwh_detect' option.\n";
305
306 if (no_fwh_detect)
307 return -ENODEV;
308 printk(warning);
309 return -EBUSY;
310 }
311
312 intel_rng_hw->mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN);
313 if (intel_rng_hw->mem == NULL)
314 return -EBUSY;
315
316 return 0;
228} 317}
229#endif 318
230 319
231static int __init mod_init(void) 320static int __init mod_init(void)
232{ 321{
233 int err = -ENODEV; 322 int err = -ENODEV;
234 unsigned i; 323 int i;
235 struct pci_dev *dev = NULL; 324 struct pci_dev *dev = NULL;
236 void __iomem *mem; 325 void __iomem *mem = mem;
237 unsigned long flags; 326 u8 hw_status;
238 u8 bios_cntl_off, fwh_dec_en1_off; 327 struct intel_rng_hw *intel_rng_hw;
239 u8 bios_cntl_val = 0xff, fwh_dec_en1_val = 0xff;
240 u8 hw_status, mfc, dvc;
241 328
242 for (i = 0; !dev && pci_tbl[i].vendor; ++i) 329 for (i = 0; !dev && pci_tbl[i].vendor; ++i)
243 dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device, NULL); 330 dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device,
331 NULL);
244 332
245 if (!dev) 333 if (!dev)
246 goto out; /* Device not found. */ 334 goto out; /* Device not found. */
@@ -250,39 +338,18 @@ static int __init mod_init(void)
250 goto fwh_done; 338 goto fwh_done;
251 } 339 }
252 340
253 /* Check for Intel 82802 */ 341 intel_rng_hw = kmalloc(sizeof(*intel_rng_hw), GFP_KERNEL);
254 if (dev->device < 0x2640) { 342 if (!intel_rng_hw) {
255 fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;
256 bios_cntl_off = BIOS_CNTL_REG_OLD;
257 } else {
258 fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;
259 bios_cntl_off = BIOS_CNTL_REG_NEW;
260 }
261
262 pci_read_config_byte(dev, fwh_dec_en1_off, &fwh_dec_en1_val);
263 pci_read_config_byte(dev, bios_cntl_off, &bios_cntl_val);
264
265 if ((bios_cntl_val &
266 (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))
267 == BIOS_CNTL_LOCK_ENABLE_MASK) {
268 static __initdata /*const*/ char warning[] =
269 KERN_WARNING PFX "Firmware space is locked read-only. If you can't or\n"
270 KERN_WARNING PFX "don't want to disable this in firmware setup, and if\n"
271 KERN_WARNING PFX "you are certain that your system has a functional\n"
272 KERN_WARNING PFX "RNG, try using the 'no_fwh_detect' option.\n";
273
274 pci_dev_put(dev); 343 pci_dev_put(dev);
275 if (no_fwh_detect)
276 goto fwh_done;
277 printk(warning);
278 err = -EBUSY;
279 goto out; 344 goto out;
280 } 345 }
281 346
282 mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN); 347 err = intel_init_hw_struct(intel_rng_hw, dev);
283 if (mem == NULL) { 348 if (err) {
284 pci_dev_put(dev); 349 pci_dev_put(dev);
285 err = -EBUSY; 350 kfree(intel_rng_hw);
351 if (err == -ENODEV)
352 goto fwh_done;
286 goto out; 353 goto out;
287 } 354 }
288 355
@@ -290,59 +357,18 @@ static int __init mod_init(void)
290 * Since the BIOS code/data is going to disappear from its normal 357 * Since the BIOS code/data is going to disappear from its normal
291 * location with the Read ID command, all activity on the system 358 * location with the Read ID command, all activity on the system
292 * must be stopped until the state is back to normal. 359 * must be stopped until the state is back to normal.
360 *
361 * Use stop_machine_run because IPIs can be blocked by disabling
362 * interrupts.
293 */ 363 */
294#ifdef CONFIG_SMP 364 err = stop_machine_run(intel_rng_hw_init, intel_rng_hw, NR_CPUS);
295 set_mb(waitflag, 1);
296 if (smp_call_function(intel_init_wait, NULL, 1, 0) != 0) {
297 set_mb(waitflag, 0);
298 pci_dev_put(dev);
299 printk(KERN_ERR PFX "cannot run on all processors\n");
300 err = -EAGAIN;
301 goto err_unmap;
302 }
303#endif
304 local_irq_save(flags);
305
306 if (!(fwh_dec_en1_val & FWH_F8_EN_MASK))
307 pci_write_config_byte(dev,
308 fwh_dec_en1_off,
309 fwh_dec_en1_val | FWH_F8_EN_MASK);
310 if (!(bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK))
311 pci_write_config_byte(dev,
312 bios_cntl_off,
313 bios_cntl_val | BIOS_CNTL_WRITE_ENABLE_MASK);
314
315 writeb(INTEL_FWH_RESET_CMD, mem);
316 writeb(INTEL_FWH_READ_ID_CMD, mem);
317 mfc = readb(mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);
318 dvc = readb(mem + INTEL_FWH_DEVICE_CODE_ADDRESS);
319 writeb(INTEL_FWH_RESET_CMD, mem);
320
321 if (!(bios_cntl_val &
322 (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
323 pci_write_config_byte(dev, bios_cntl_off, bios_cntl_val);
324 if (!(fwh_dec_en1_val & FWH_F8_EN_MASK))
325 pci_write_config_byte(dev, fwh_dec_en1_off, fwh_dec_en1_val);
326
327 local_irq_restore(flags);
328#ifdef CONFIG_SMP
329 /* Tell other CPUs to resume. */
330 set_mb(waitflag, 0);
331#endif
332
333 iounmap(mem);
334 pci_dev_put(dev); 365 pci_dev_put(dev);
335 366 iounmap(intel_rng_hw->mem);
336 if (mfc != INTEL_FWH_MANUFACTURER_CODE || 367 kfree(intel_rng_hw);
337 (dvc != INTEL_FWH_DEVICE_CODE_8M && 368 if (err)
338 dvc != INTEL_FWH_DEVICE_CODE_4M)) {
339 printk(KERN_ERR PFX "FWH not detected\n");
340 err = -ENODEV;
341 goto out; 369 goto out;
342 }
343 370
344fwh_done: 371fwh_done:
345
346 err = -ENOMEM; 372 err = -ENOMEM;
347 mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); 373 mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);
348 if (!mem) 374 if (!mem)
@@ -352,22 +378,21 @@ fwh_done:
352 /* Check for Random Number Generator */ 378 /* Check for Random Number Generator */
353 err = -ENODEV; 379 err = -ENODEV;
354 hw_status = hwstatus_get(mem); 380 hw_status = hwstatus_get(mem);
355 if ((hw_status & INTEL_RNG_PRESENT) == 0) 381 if ((hw_status & INTEL_RNG_PRESENT) == 0) {
356 goto err_unmap; 382 iounmap(mem);
383 goto out;
384 }
357 385
358 printk(KERN_INFO "Intel 82802 RNG detected\n"); 386 printk(KERN_INFO "Intel 82802 RNG detected\n");
359 err = hwrng_register(&intel_rng); 387 err = hwrng_register(&intel_rng);
360 if (err) { 388 if (err) {
361 printk(KERN_ERR PFX "RNG registering failed (%d)\n", 389 printk(KERN_ERR PFX "RNG registering failed (%d)\n",
362 err); 390 err);
363 goto err_unmap; 391 iounmap(mem);
364 } 392 }
365out: 393out:
366 return err; 394 return err;
367 395
368err_unmap:
369 iounmap(mem);
370 goto out;
371} 396}
372 397
373static void __exit mod_exit(void) 398static void __exit mod_exit(void)