brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · 7b171cb Raw
419 lines · c
1/*2 * RNG driver for Intel RNGs3 *4 * Copyright 2005 (c) MontaVista Software, Inc.5 *6 * with the majority of the code coming from:7 *8 * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)9 * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>10 *11 * derived from12 *13 * Hardware driver for the AMD 768 Random Number Generator (RNG)14 * (c) Copyright 2001 Red Hat Inc15 *16 * derived from17 *18 * Hardware driver for Intel i810 Random Number Generator (RNG)19 * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>20 * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>21 *22 * This file is licensed under  the terms of the GNU General Public23 * License version 2. This program is licensed "as is" without any24 * warranty of any kind, whether express or implied.25 */26 27#include <linux/hw_random.h>28#include <linux/io.h>29#include <linux/kernel.h>30#include <linux/module.h>31#include <linux/pci.h>32#include <linux/stop_machine.h>33#include <linux/delay.h>34#include <linux/slab.h>35 36 37#define PFX	KBUILD_MODNAME ": "38 39/*40 * RNG registers41 */42#define INTEL_RNG_HW_STATUS			043#define         INTEL_RNG_PRESENT		0x4044#define         INTEL_RNG_ENABLED		0x0145#define INTEL_RNG_STATUS			146#define         INTEL_RNG_DATA_PRESENT		0x0147#define INTEL_RNG_DATA				248 49/*50 * Magic address at which Intel PCI bridges locate the RNG51 */52#define INTEL_RNG_ADDR				0xFFBC015F53#define INTEL_RNG_ADDR_LEN			354 55/*56 * LPC bridge PCI config space registers57 */58#define FWH_DEC_EN1_REG_OLD			0xe359#define FWH_DEC_EN1_REG_NEW			0xd9 /* high byte of 16-bit register */60#define FWH_F8_EN_MASK				0x8061 62#define BIOS_CNTL_REG_OLD			0x4e63#define BIOS_CNTL_REG_NEW			0xdc64#define BIOS_CNTL_WRITE_ENABLE_MASK		0x0165#define BIOS_CNTL_LOCK_ENABLE_MASK		0x0266 67/*68 * Magic address at which Intel Firmware Hubs get accessed69 */70#define INTEL_FWH_ADDR				0xffff000071#define INTEL_FWH_ADDR_LEN			272 73/*74 * Intel Firmware Hub command codes (write to any address inside the device)75 */76#define INTEL_FWH_RESET_CMD			0xff /* aka READ_ARRAY */77#define INTEL_FWH_READ_ID_CMD			0x9078 79/*80 * Intel Firmware Hub Read ID command result addresses81 */82#define INTEL_FWH_MANUFACTURER_CODE_ADDRESS	0x00000083#define INTEL_FWH_DEVICE_CODE_ADDRESS		0x00000184 85/*86 * Intel Firmware Hub Read ID command result values87 */88#define INTEL_FWH_MANUFACTURER_CODE		0x8989#define INTEL_FWH_DEVICE_CODE_8M		0xac90#define INTEL_FWH_DEVICE_CODE_4M		0xad91 92/*93 * Data for PCI driver interface94 *95 * This data only exists for exporting the supported96 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually97 * register a pci_driver, because someone else might one day98 * want to register another driver on the same PCI id.99 */100static const struct pci_device_id pci_tbl[] = {101/* AA102	{ PCI_DEVICE(0x8086, 0x2418) }, */103	{ PCI_DEVICE(0x8086, 0x2410) }, /* AA */104/* AB105	{ PCI_DEVICE(0x8086, 0x2428) }, */106	{ PCI_DEVICE(0x8086, 0x2420) }, /* AB */107/* ??108	{ PCI_DEVICE(0x8086, 0x2430) }, */109/* BAM, CAM, DBM, FBM, GxM110	{ PCI_DEVICE(0x8086, 0x2448) }, */111	{ PCI_DEVICE(0x8086, 0x244c) }, /* BAM */112	{ PCI_DEVICE(0x8086, 0x248c) }, /* CAM */113	{ PCI_DEVICE(0x8086, 0x24cc) }, /* DBM */114	{ PCI_DEVICE(0x8086, 0x2641) }, /* FBM */115	{ PCI_DEVICE(0x8086, 0x27b9) }, /* GxM */116	{ PCI_DEVICE(0x8086, 0x27bd) }, /* GxM DH */117/* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx118	{ PCI_DEVICE(0x8086, 0x244e) }, */119	{ PCI_DEVICE(0x8086, 0x2440) }, /* BA */120	{ PCI_DEVICE(0x8086, 0x2480) }, /* CA */121	{ PCI_DEVICE(0x8086, 0x24c0) }, /* DB */122	{ PCI_DEVICE(0x8086, 0x24d0) }, /* Ex */123	{ PCI_DEVICE(0x8086, 0x25a1) }, /* 6300 */124	{ PCI_DEVICE(0x8086, 0x2640) }, /* Fx */125	{ PCI_DEVICE(0x8086, 0x2670) }, /* 631x/632x */126	{ PCI_DEVICE(0x8086, 0x2671) }, /* 631x/632x */127	{ PCI_DEVICE(0x8086, 0x2672) }, /* 631x/632x */128	{ PCI_DEVICE(0x8086, 0x2673) }, /* 631x/632x */129	{ PCI_DEVICE(0x8086, 0x2674) }, /* 631x/632x */130	{ PCI_DEVICE(0x8086, 0x2675) }, /* 631x/632x */131	{ PCI_DEVICE(0x8086, 0x2676) }, /* 631x/632x */132	{ PCI_DEVICE(0x8086, 0x2677) }, /* 631x/632x */133	{ PCI_DEVICE(0x8086, 0x2678) }, /* 631x/632x */134	{ PCI_DEVICE(0x8086, 0x2679) }, /* 631x/632x */135	{ PCI_DEVICE(0x8086, 0x267a) }, /* 631x/632x */136	{ PCI_DEVICE(0x8086, 0x267b) }, /* 631x/632x */137	{ PCI_DEVICE(0x8086, 0x267c) }, /* 631x/632x */138	{ PCI_DEVICE(0x8086, 0x267d) }, /* 631x/632x */139	{ PCI_DEVICE(0x8086, 0x267e) }, /* 631x/632x */140	{ PCI_DEVICE(0x8086, 0x267f) }, /* 631x/632x */141	{ PCI_DEVICE(0x8086, 0x27b8) }, /* Gx */142/* E143	{ PCI_DEVICE(0x8086, 0x245e) }, */144	{ PCI_DEVICE(0x8086, 0x2450) }, /* E  */145	{ 0, },	/* terminate list */146};147MODULE_DEVICE_TABLE(pci, pci_tbl);148 149static __initdata int no_fwh_detect;150module_param(no_fwh_detect, int, 0);151MODULE_PARM_DESC(no_fwh_detect, "Skip FWH detection:\n"152                                " positive value - skip if FWH space locked read-only\n"153                                " negative value - skip always");154 155static inline u8 hwstatus_get(void __iomem *mem)156{157	return readb(mem + INTEL_RNG_HW_STATUS);158}159 160static inline u8 hwstatus_set(void __iomem *mem,161			      u8 hw_status)162{163	writeb(hw_status, mem + INTEL_RNG_HW_STATUS);164	return hwstatus_get(mem);165}166 167static int intel_rng_data_present(struct hwrng *rng, int wait)168{169	void __iomem *mem = (void __iomem *)rng->priv;170	int data, i;171 172	for (i = 0; i < 20; i++) {173		data = !!(readb(mem + INTEL_RNG_STATUS) &174			  INTEL_RNG_DATA_PRESENT);175		if (data || !wait)176			break;177		udelay(10);178	}179	return data;180}181 182static int intel_rng_data_read(struct hwrng *rng, u32 *data)183{184	void __iomem *mem = (void __iomem *)rng->priv;185 186	*data = readb(mem + INTEL_RNG_DATA);187 188	return 1;189}190 191static int intel_rng_init(struct hwrng *rng)192{193	void __iomem *mem = (void __iomem *)rng->priv;194	u8 hw_status;195	int err = -EIO;196 197	hw_status = hwstatus_get(mem);198	/* turn RNG h/w on, if it's off */199	if ((hw_status & INTEL_RNG_ENABLED) == 0)200		hw_status = hwstatus_set(mem, hw_status | INTEL_RNG_ENABLED);201	if ((hw_status & INTEL_RNG_ENABLED) == 0) {202		pr_err(PFX "cannot enable RNG, aborting\n");203		goto out;204	}205	err = 0;206out:207	return err;208}209 210static void intel_rng_cleanup(struct hwrng *rng)211{212	void __iomem *mem = (void __iomem *)rng->priv;213	u8 hw_status;214 215	hw_status = hwstatus_get(mem);216	if (hw_status & INTEL_RNG_ENABLED)217		hwstatus_set(mem, hw_status & ~INTEL_RNG_ENABLED);218	else219		pr_warn(PFX "unusual: RNG already disabled\n");220}221 222 223static struct hwrng intel_rng = {224	.name		= "intel",225	.init		= intel_rng_init,226	.cleanup	= intel_rng_cleanup,227	.data_present	= intel_rng_data_present,228	.data_read	= intel_rng_data_read,229};230 231struct intel_rng_hw {232	struct pci_dev *dev;233	void __iomem *mem;234	u8 bios_cntl_off;235	u8 bios_cntl_val;236	u8 fwh_dec_en1_off;237	u8 fwh_dec_en1_val;238};239 240static int __init intel_rng_hw_init(void *_intel_rng_hw)241{242	struct intel_rng_hw *intel_rng_hw = _intel_rng_hw;243	u8 mfc, dvc;244 245	/* interrupts disabled in stop_machine call */246 247	if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))248		pci_write_config_byte(intel_rng_hw->dev,249		                      intel_rng_hw->fwh_dec_en1_off,250		                      intel_rng_hw->fwh_dec_en1_val |251				      FWH_F8_EN_MASK);252	if (!(intel_rng_hw->bios_cntl_val & BIOS_CNTL_WRITE_ENABLE_MASK))253		pci_write_config_byte(intel_rng_hw->dev,254		                      intel_rng_hw->bios_cntl_off,255		                      intel_rng_hw->bios_cntl_val |256				      BIOS_CNTL_WRITE_ENABLE_MASK);257 258	writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);259	writeb(INTEL_FWH_READ_ID_CMD, intel_rng_hw->mem);260	mfc = readb(intel_rng_hw->mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);261	dvc = readb(intel_rng_hw->mem + INTEL_FWH_DEVICE_CODE_ADDRESS);262	writeb(INTEL_FWH_RESET_CMD, intel_rng_hw->mem);263 264	if (!(intel_rng_hw->bios_cntl_val &265	      (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))266		pci_write_config_byte(intel_rng_hw->dev,267				      intel_rng_hw->bios_cntl_off,268				      intel_rng_hw->bios_cntl_val);269	if (!(intel_rng_hw->fwh_dec_en1_val & FWH_F8_EN_MASK))270		pci_write_config_byte(intel_rng_hw->dev,271				      intel_rng_hw->fwh_dec_en1_off,272				      intel_rng_hw->fwh_dec_en1_val);273 274	if (mfc != INTEL_FWH_MANUFACTURER_CODE ||275	    (dvc != INTEL_FWH_DEVICE_CODE_8M &&276	     dvc != INTEL_FWH_DEVICE_CODE_4M)) {277		pr_notice(PFX "FWH not detected\n");278		return -ENODEV;279	}280 281	return 0;282}283 284static int __init intel_init_hw_struct(struct intel_rng_hw *intel_rng_hw,285					struct pci_dev *dev)286{287	intel_rng_hw->bios_cntl_val = 0xff;288	intel_rng_hw->fwh_dec_en1_val = 0xff;289	intel_rng_hw->dev = dev;290 291	/* Check for Intel 82802 */292	if (dev->device < 0x2640) {293		intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;294		intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_OLD;295	} else {296		intel_rng_hw->fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;297		intel_rng_hw->bios_cntl_off = BIOS_CNTL_REG_NEW;298	}299 300	pci_read_config_byte(dev, intel_rng_hw->fwh_dec_en1_off,301			     &intel_rng_hw->fwh_dec_en1_val);302	pci_read_config_byte(dev, intel_rng_hw->bios_cntl_off,303			     &intel_rng_hw->bios_cntl_val);304 305	if ((intel_rng_hw->bios_cntl_val &306	     (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))307	    == BIOS_CNTL_LOCK_ENABLE_MASK) {308		static __initdata /*const*/ char warning[] =309PFX "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";313 314		if (no_fwh_detect)315			return -ENODEV;316		pr_warn("%s", warning);317		return -EBUSY;318	}319 320	intel_rng_hw->mem = ioremap(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN);321	if (intel_rng_hw->mem == NULL)322		return -EBUSY;323 324	return 0;325}326 327 328static int __init intel_rng_mod_init(void)329{330	int err = -ENODEV;331	int i;332	struct pci_dev *dev = NULL;333	void __iomem *mem;334	u8 hw_status;335	struct intel_rng_hw *intel_rng_hw;336 337	for (i = 0; !dev && pci_tbl[i].vendor; ++i)338		dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device,339				     NULL);340 341	if (!dev)342		goto out; /* Device not found. */343 344	if (no_fwh_detect < 0) {345		pci_dev_put(dev);346		goto fwh_done;347	}348 349	intel_rng_hw = kmalloc(sizeof(*intel_rng_hw), GFP_KERNEL);350	if (!intel_rng_hw) {351		pci_dev_put(dev);352		goto out;353	}354 355	err = intel_init_hw_struct(intel_rng_hw, dev);356	if (err) {357		pci_dev_put(dev);358		kfree(intel_rng_hw);359		if (err == -ENODEV)360			goto fwh_done;361		goto out;362	}363 364	/*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	 */372	err = stop_machine(intel_rng_hw_init, intel_rng_hw, NULL);373	pci_dev_put(dev);374	iounmap(intel_rng_hw->mem);375	kfree(intel_rng_hw);376	if (err)377		goto out;378 379fwh_done:380	err = -ENOMEM;381	mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);382	if (!mem)383		goto out;384	intel_rng.priv = (unsigned long)mem;385 386	/* Check for Random Number Generator */387	err = -ENODEV;388	hw_status = hwstatus_get(mem);389	if ((hw_status & INTEL_RNG_PRESENT) == 0) {390		iounmap(mem);391		goto out;392	}393 394	pr_info("Intel 82802 RNG detected\n");395	err = hwrng_register(&intel_rng);396	if (err) {397		pr_err(PFX "RNG registering failed (%d)\n",398		       err);399		iounmap(mem);400	}401out:402	return err;403 404}405 406static void __exit intel_rng_mod_exit(void)407{408	void __iomem *mem = (void __iomem *)intel_rng.priv;409 410	hwrng_unregister(&intel_rng);411	iounmap(mem);412}413 414module_init(intel_rng_mod_init);415module_exit(intel_rng_mod_exit);416 417MODULE_DESCRIPTION("H/W RNG driver for Intel chipsets");418MODULE_LICENSE("GPL");419