Path: blob/master/drivers/char/hw_random/intel-rng.c
15111 views
/*1* RNG driver for Intel RNGs2*3* Copyright 2005 (c) MontaVista Software, Inc.4*5* with the majority of the code coming from:6*7* Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)8* (c) Copyright 2003 Red Hat Inc <[email protected]>9*10* derived from11*12* Hardware driver for the AMD 768 Random Number Generator (RNG)13* (c) Copyright 2001 Red Hat Inc14*15* derived from16*17* Hardware driver for Intel i810 Random Number Generator (RNG)18* Copyright 2000,2001 Jeff Garzik <[email protected]>19* Copyright 2000,2001 Philipp Rumpf <[email protected]>20*21* This file is licensed under the terms of the GNU General Public22* License version 2. This program is licensed "as is" without any23* warranty of any kind, whether express or implied.24*/2526#include <linux/hw_random.h>27#include <linux/kernel.h>28#include <linux/module.h>29#include <linux/pci.h>30#include <linux/stop_machine.h>31#include <linux/delay.h>32#include <linux/slab.h>33#include <asm/io.h>343536#define PFX KBUILD_MODNAME ": "3738/*39* RNG registers40*/41#define INTEL_RNG_HW_STATUS 042#define INTEL_RNG_PRESENT 0x4043#define INTEL_RNG_ENABLED 0x0144#define INTEL_RNG_STATUS 145#define INTEL_RNG_DATA_PRESENT 0x0146#define INTEL_RNG_DATA 24748/*49* Magic address at which Intel PCI bridges locate the RNG50*/51#define INTEL_RNG_ADDR 0xFFBC015F52#define INTEL_RNG_ADDR_LEN 35354/*55* LPC bridge PCI config space registers56*/57#define FWH_DEC_EN1_REG_OLD 0xe358#define FWH_DEC_EN1_REG_NEW 0xd9 /* high byte of 16-bit register */59#define FWH_F8_EN_MASK 0x806061#define BIOS_CNTL_REG_OLD 0x4e62#define BIOS_CNTL_REG_NEW 0xdc63#define BIOS_CNTL_WRITE_ENABLE_MASK 0x0164#define BIOS_CNTL_LOCK_ENABLE_MASK 0x026566/*67* Magic address at which Intel Firmware Hubs get accessed68*/69#define INTEL_FWH_ADDR 0xffff000070#define INTEL_FWH_ADDR_LEN 27172/*73* Intel Firmware Hub command codes (write to any address inside the device)74*/75#define INTEL_FWH_RESET_CMD 0xff /* aka READ_ARRAY */76#define INTEL_FWH_READ_ID_CMD 0x907778/*79* Intel Firmware Hub Read ID command result addresses80*/81#define INTEL_FWH_MANUFACTURER_CODE_ADDRESS 0x00000082#define INTEL_FWH_DEVICE_CODE_ADDRESS 0x0000018384/*85* Intel Firmware Hub Read ID command result values86*/87#define INTEL_FWH_MANUFACTURER_CODE 0x8988#define INTEL_FWH_DEVICE_CODE_8M 0xac89#define INTEL_FWH_DEVICE_CODE_4M 0xad9091/*92* Data for PCI driver interface93*94* This data only exists for exporting the supported95* PCI ids via MODULE_DEVICE_TABLE. We do not actually96* register a pci_driver, because someone else might one day97* want to register another driver on the same PCI id.98*/99static const struct pci_device_id pci_tbl[] = {100/* AA101{ PCI_DEVICE(0x8086, 0x2418) }, */102{ PCI_DEVICE(0x8086, 0x2410) }, /* AA */103/* AB104{ PCI_DEVICE(0x8086, 0x2428) }, */105{ PCI_DEVICE(0x8086, 0x2420) }, /* AB */106/* ??107{ PCI_DEVICE(0x8086, 0x2430) }, */108/* BAM, CAM, DBM, FBM, GxM109{ PCI_DEVICE(0x8086, 0x2448) }, */110{ PCI_DEVICE(0x8086, 0x244c) }, /* BAM */111{ PCI_DEVICE(0x8086, 0x248c) }, /* CAM */112{ PCI_DEVICE(0x8086, 0x24cc) }, /* DBM */113{ PCI_DEVICE(0x8086, 0x2641) }, /* FBM */114{ PCI_DEVICE(0x8086, 0x27b9) }, /* GxM */115{ PCI_DEVICE(0x8086, 0x27bd) }, /* GxM DH */116/* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx117{ PCI_DEVICE(0x8086, 0x244e) }, */118{ PCI_DEVICE(0x8086, 0x2440) }, /* BA */119{ PCI_DEVICE(0x8086, 0x2480) }, /* CA */120{ PCI_DEVICE(0x8086, 0x24c0) }, /* DB */121{ PCI_DEVICE(0x8086, 0x24d0) }, /* Ex */122{ PCI_DEVICE(0x8086, 0x25a1) }, /* 6300 */123{ PCI_DEVICE(0x8086, 0x2640) }, /* Fx */124{ PCI_DEVICE(0x8086, 0x2670) }, /* 631x/632x */125{ PCI_DEVICE(0x8086, 0x2671) }, /* 631x/632x */126{ PCI_DEVICE(0x8086, 0x2672) }, /* 631x/632x */127{ PCI_DEVICE(0x8086, 0x2673) }, /* 631x/632x */128{ PCI_DEVICE(0x8086, 0x2674) }, /* 631x/632x */129{ PCI_DEVICE(0x8086, 0x2675) }, /* 631x/632x */130{ PCI_DEVICE(0x8086, 0x2676) }, /* 631x/632x */131{ PCI_DEVICE(0x8086, 0x2677) }, /* 631x/632x */132{ PCI_DEVICE(0x8086, 0x2678) }, /* 631x/632x */133{ PCI_DEVICE(0x8086, 0x2679) }, /* 631x/632x */134{ PCI_DEVICE(0x8086, 0x267a) }, /* 631x/632x */135{ PCI_DEVICE(0x8086, 0x267b) }, /* 631x/632x */136{ PCI_DEVICE(0x8086, 0x267c) }, /* 631x/632x */137{ PCI_DEVICE(0x8086, 0x267d) }, /* 631x/632x */138{ PCI_DEVICE(0x8086, 0x267e) }, /* 631x/632x */139{ PCI_DEVICE(0x8086, 0x267f) }, /* 631x/632x */140{ PCI_DEVICE(0x8086, 0x27b8) }, /* Gx */141/* E142{ PCI_DEVICE(0x8086, 0x245e) }, */143{ PCI_DEVICE(0x8086, 0x2450) }, /* E */144{ 0, }, /* terminate list */145};146MODULE_DEVICE_TABLE(pci, pci_tbl);147148static __initdata int no_fwh_detect;149module_param(no_fwh_detect, int, 0);150MODULE_PARM_DESC(no_fwh_detect, "Skip FWH detection:\n"151" positive value - skip if FWH space locked read-only\n"152" negative value - skip always");153154static inline u8 hwstatus_get(void __iomem *mem)155{156return readb(mem + INTEL_RNG_HW_STATUS);157}158159static inline u8 hwstatus_set(void __iomem *mem,160u8 hw_status)161{162writeb(hw_status, mem + INTEL_RNG_HW_STATUS);163return hwstatus_get(mem);164}165166static int intel_rng_data_present(struct hwrng *rng, int wait)167{168void __iomem *mem = (void __iomem *)rng->priv;169int data, i;170171for (i = 0; i < 20; i++) {172data = !!(readb(mem + INTEL_RNG_STATUS) &173INTEL_RNG_DATA_PRESENT);174if (data || !wait)175break;176udelay(10);177}178return data;179}180181static int intel_rng_data_read(struct hwrng *rng, u32 *data)182{183void __iomem *mem = (void __iomem *)rng->priv;184185*data = readb(mem + INTEL_RNG_DATA);186187return 1;188}189190static int intel_rng_init(struct hwrng *rng)191{192void __iomem *mem = (void __iomem *)rng->priv;193u8 hw_status;194int err = -EIO;195196hw_status = hwstatus_get(mem);197/* turn RNG h/w on, if it's off */198if ((hw_status & INTEL_RNG_ENABLED) == 0)199hw_status = hwstatus_set(mem, hw_status | INTEL_RNG_ENABLED);200if ((hw_status & INTEL_RNG_ENABLED) == 0) {201printk(KERN_ERR PFX "cannot enable RNG, aborting\n");202goto out;203}204err = 0;205out:206return err;207}208209static void intel_rng_cleanup(struct hwrng *rng)210{211void __iomem *mem = (void __iomem *)rng->priv;212u8 hw_status;213214hw_status = hwstatus_get(mem);215if (hw_status & INTEL_RNG_ENABLED)216hwstatus_set(mem, hw_status & ~INTEL_RNG_ENABLED);217else218printk(KERN_WARNING PFX "unusual: RNG already disabled\n");219}220221222static struct hwrng intel_rng = {223.name = "intel",224.init = intel_rng_init,225.cleanup = intel_rng_cleanup,226.data_present = intel_rng_data_present,227.data_read = intel_rng_data_read,228};229230struct intel_rng_hw {231struct pci_dev *dev;232void __iomem *mem;233u8 bios_cntl_off;234u8 bios_cntl_val;235u8 fwh_dec_en1_off;236u8 fwh_dec_en1_val;237};238239static int __init intel_rng_hw_init(void *_intel_rng_hw)240{241struct intel_rng_hw *intel_rng_hw = _intel_rng_hw;242u8 mfc, dvc;243244/* interrupts disabled in stop_machine call */245246if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))247pci_write_config_byte(intel_rng_hw->dev,248intel_rng_hw->fwh_dec_en1_off,249intel_rng_hw->fwh_dec_en1_val |250FWH_F8_EN_MASK);251if (!(intel_rng_hw->bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK))252pci_write_config_byte(intel_rng_hw->dev,253intel_rng_hw->bios_cntl_off,254intel_rng_hw->bios_cntl_val |255BIOS_CNTL_WRITE_ENABLE_MASK);256257writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);258writeb(INTEL_FWH_READ_ID_CMD, intel_rng_hw->mem);259mfc = readb(intel_rng_hw->mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);260dvc = readb(intel_rng_hw->mem + INTEL_FWH_DEVICE_CODE_ADDRESS);261writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);262263if (!(intel_rng_hw->bios_cntl_val &264(BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))265pci_write_config_byte(intel_rng_hw->dev,266intel_rng_hw->bios_cntl_off,267intel_rng_hw->bios_cntl_val);268if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))269pci_write_config_byte(intel_rng_hw->dev,270intel_rng_hw->fwh_dec_en1_off,271intel_rng_hw->fwh_dec_en1_val);272273if (mfc != INTEL_FWH_MANUFACTURER_CODE ||274(dvc != INTEL_FWH_DEVICE_CODE_8M &&275dvc != INTEL_FWH_DEVICE_CODE_4M)) {276printk(KERN_NOTICE PFX "FWH not detected\n");277return -ENODEV;278}279280return 0;281}282283static int __init intel_init_hw_struct(struct intel_rng_hw *intel_rng_hw,284struct pci_dev *dev)285{286intel_rng_hw->bios_cntl_val = 0xff;287intel_rng_hw->fwh_dec_en1_val = 0xff;288intel_rng_hw->dev = dev;289290/* Check for Intel 82802 */291if (dev->device < 0x2640) {292intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;293intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_OLD;294} else {295intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;296intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_NEW;297}298299pci_read_config_byte(dev, intel_rng_hw->fwh_dec_en1_off,300&intel_rng_hw->fwh_dec_en1_val);301pci_read_config_byte(dev, intel_rng_hw->bios_cntl_off,302&intel_rng_hw->bios_cntl_val);303304if ((intel_rng_hw->bios_cntl_val &305(BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))306== BIOS_CNTL_LOCK_ENABLE_MASK) {307static __initdata /*const*/ char warning[] =308KERN_WARNING309PFX "Firmware space is locked read-only. If you can't or\n"310PFX "don't want to disable this in firmware setup, and if\n"311PFX "you are certain that your system has a functional\n"312PFX "RNG, try using the 'no_fwh_detect' option.\n";313314if (no_fwh_detect)315return -ENODEV;316printk(warning);317return -EBUSY;318}319320intel_rng_hw->mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN);321if (intel_rng_hw->mem == NULL)322return -EBUSY;323324return 0;325}326327328static int __init mod_init(void)329{330int err = -ENODEV;331int i;332struct pci_dev *dev = NULL;333void __iomem *mem = mem;334u8 hw_status;335struct intel_rng_hw *intel_rng_hw;336337for (i = 0; !dev && pci_tbl[i].vendor; ++i)338dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device,339NULL);340341if (!dev)342goto out; /* Device not found. */343344if (no_fwh_detect < 0) {345pci_dev_put(dev);346goto fwh_done;347}348349intel_rng_hw = kmalloc(sizeof(*intel_rng_hw), GFP_KERNEL);350if (!intel_rng_hw) {351pci_dev_put(dev);352goto out;353}354355err = intel_init_hw_struct(intel_rng_hw, dev);356if (err) {357pci_dev_put(dev);358kfree(intel_rng_hw);359if (err == -ENODEV)360goto fwh_done;361goto out;362}363364/*365* Since the BIOS code/data is going to disappear from its normal366* location with the Read ID command, all activity on the system367* must be stopped until the state is back to normal.368*369* Use stop_machine because IPIs can be blocked by disabling370* interrupts.371*/372err = stop_machine(intel_rng_hw_init, intel_rng_hw, NULL);373pci_dev_put(dev);374iounmap(intel_rng_hw->mem);375kfree(intel_rng_hw);376if (err)377goto out;378379fwh_done:380err = -ENOMEM;381mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);382if (!mem)383goto out;384intel_rng.priv = (unsigned long)mem;385386/* Check for Random Number Generator */387err = -ENODEV;388hw_status = hwstatus_get(mem);389if ((hw_status & INTEL_RNG_PRESENT) == 0) {390iounmap(mem);391goto out;392}393394printk(KERN_INFO "Intel 82802 RNG detected\n");395err = hwrng_register(&intel_rng);396if (err) {397printk(KERN_ERR PFX "RNG registering failed (%d)\n",398err);399iounmap(mem);400}401out:402return err;403404}405406static void __exit mod_exit(void)407{408void __iomem *mem = (void __iomem *)intel_rng.priv;409410hwrng_unregister(&intel_rng);411iounmap(mem);412}413414module_init(mod_init);415module_exit(mod_exit);416417MODULE_DESCRIPTION("H/W RNG driver for Intel chipsets");418MODULE_LICENSE("GPL");419420421