cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

amd-rng.c (4786B)


      1/*
      2 * RNG driver for AMD RNGs
      3 *
      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 from
     12 *
     13 * Hardware driver for the AMD 768 Random Number Generator (RNG)
     14 * (c) Copyright 2001 Red Hat Inc
     15 *
     16 * derived from
     17 *
     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 Public
     23 * License version 2. This program is licensed "as is" without any
     24 * warranty of any kind, whether express or implied.
     25 */
     26
     27#include <linux/delay.h>
     28#include <linux/hw_random.h>
     29#include <linux/kernel.h>
     30#include <linux/module.h>
     31#include <linux/pci.h>
     32
     33#define DRV_NAME "AMD768-HWRNG"
     34
     35#define RNGDATA		0x00
     36#define RNGDONE		0x04
     37#define PMBASE_OFFSET	0xF0
     38#define PMBASE_SIZE	8
     39
     40/*
     41 * Data for PCI driver interface
     42 *
     43 * This data only exists for exporting the supported
     44 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
     45 * register a pci_driver, because someone else might one day
     46 * want to register another driver on the same PCI id.
     47 */
     48static const struct pci_device_id pci_tbl[] = {
     49	{ PCI_VDEVICE(AMD, 0x7443), 0, },
     50	{ PCI_VDEVICE(AMD, 0x746b), 0, },
     51	{ 0, },	/* terminate list */
     52};
     53MODULE_DEVICE_TABLE(pci, pci_tbl);
     54
     55struct amd768_priv {
     56	void __iomem *iobase;
     57	struct pci_dev *pcidev;
     58	u32 pmbase;
     59};
     60
     61static int amd_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
     62{
     63	u32 *data = buf;
     64	struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
     65	size_t read = 0;
     66	/* We will wait at maximum one time per read */
     67	int timeout = max / 4 + 1;
     68
     69	/*
     70	 * RNG data is available when RNGDONE is set to 1
     71	 * New random numbers are generated approximately 128 microseconds
     72	 * after RNGDATA is read
     73	 */
     74	while (read < max) {
     75		if (ioread32(priv->iobase + RNGDONE) == 0) {
     76			if (wait) {
     77				/* Delay given by datasheet */
     78				usleep_range(128, 196);
     79				if (timeout-- == 0)
     80					return read;
     81			} else {
     82				return 0;
     83			}
     84		} else {
     85			*data = ioread32(priv->iobase + RNGDATA);
     86			data++;
     87			read += 4;
     88		}
     89	}
     90
     91	return read;
     92}
     93
     94static int amd_rng_init(struct hwrng *rng)
     95{
     96	struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
     97	u8 rnen;
     98
     99	pci_read_config_byte(priv->pcidev, 0x40, &rnen);
    100	rnen |= BIT(7);	/* RNG on */
    101	pci_write_config_byte(priv->pcidev, 0x40, rnen);
    102
    103	pci_read_config_byte(priv->pcidev, 0x41, &rnen);
    104	rnen |= BIT(7);	/* PMIO enable */
    105	pci_write_config_byte(priv->pcidev, 0x41, rnen);
    106
    107	return 0;
    108}
    109
    110static void amd_rng_cleanup(struct hwrng *rng)
    111{
    112	struct amd768_priv *priv = (struct amd768_priv *)rng->priv;
    113	u8 rnen;
    114
    115	pci_read_config_byte(priv->pcidev, 0x40, &rnen);
    116	rnen &= ~BIT(7);	/* RNG off */
    117	pci_write_config_byte(priv->pcidev, 0x40, rnen);
    118}
    119
    120static struct hwrng amd_rng = {
    121	.name		= "amd",
    122	.init		= amd_rng_init,
    123	.cleanup	= amd_rng_cleanup,
    124	.read		= amd_rng_read,
    125};
    126
    127static int __init amd_rng_mod_init(void)
    128{
    129	int err;
    130	struct pci_dev *pdev = NULL;
    131	const struct pci_device_id *ent;
    132	u32 pmbase;
    133	struct amd768_priv *priv;
    134
    135	for_each_pci_dev(pdev) {
    136		ent = pci_match_id(pci_tbl, pdev);
    137		if (ent)
    138			goto found;
    139	}
    140	/* Device not found. */
    141	return -ENODEV;
    142
    143found:
    144	err = pci_read_config_dword(pdev, 0x58, &pmbase);
    145	if (err)
    146		return err;
    147
    148	pmbase &= 0x0000FF00;
    149	if (pmbase == 0)
    150		return -EIO;
    151
    152	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    153	if (!priv)
    154		return -ENOMEM;
    155
    156	if (!request_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE, DRV_NAME)) {
    157		dev_err(&pdev->dev, DRV_NAME " region 0x%x already in use!\n",
    158			pmbase + 0xF0);
    159		err = -EBUSY;
    160		goto out;
    161	}
    162
    163	priv->iobase = ioport_map(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
    164	if (!priv->iobase) {
    165		pr_err(DRV_NAME "Cannot map ioport\n");
    166		err = -EINVAL;
    167		goto err_iomap;
    168	}
    169
    170	amd_rng.priv = (unsigned long)priv;
    171	priv->pmbase = pmbase;
    172	priv->pcidev = pdev;
    173
    174	pr_info(DRV_NAME " detected\n");
    175	err = hwrng_register(&amd_rng);
    176	if (err) {
    177		pr_err(DRV_NAME " registering failed (%d)\n", err);
    178		goto err_hwrng;
    179	}
    180	return 0;
    181
    182err_hwrng:
    183	ioport_unmap(priv->iobase);
    184err_iomap:
    185	release_region(pmbase + PMBASE_OFFSET, PMBASE_SIZE);
    186out:
    187	kfree(priv);
    188	return err;
    189}
    190
    191static void __exit amd_rng_mod_exit(void)
    192{
    193	struct amd768_priv *priv;
    194
    195	priv = (struct amd768_priv *)amd_rng.priv;
    196
    197	hwrng_unregister(&amd_rng);
    198
    199	ioport_unmap(priv->iobase);
    200
    201	release_region(priv->pmbase + PMBASE_OFFSET, PMBASE_SIZE);
    202
    203	kfree(priv);
    204}
    205
    206module_init(amd_rng_mod_init);
    207module_exit(amd_rng_mod_exit);
    208
    209MODULE_AUTHOR("The Linux Kernel team");
    210MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
    211MODULE_LICENSE("GPL");