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

cn10k-rng.c (4050B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Marvell CN10K RVU Hardware Random Number Generator.
      3 *
      4 * Copyright (C) 2021 Marvell.
      5 *
      6 */
      7
      8#include <linux/hw_random.h>
      9#include <linux/io.h>
     10#include <linux/module.h>
     11#include <linux/pci.h>
     12#include <linux/pci_ids.h>
     13#include <linux/delay.h>
     14
     15#include <linux/arm-smccc.h>
     16
     17/* CSRs */
     18#define RNM_CTL_STATUS		0x000
     19#define RNM_ENTROPY_STATUS	0x008
     20#define RNM_CONST		0x030
     21#define RNM_EBG_ENT		0x048
     22#define RNM_PF_EBG_HEALTH	0x050
     23#define RNM_PF_RANDOM		0x400
     24#define RNM_TRNG_RESULT		0x408
     25
     26struct cn10k_rng {
     27	void __iomem *reg_base;
     28	struct hwrng ops;
     29	struct pci_dev *pdev;
     30};
     31
     32#define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE     0xc2000b0f
     33
     34static unsigned long reset_rng_health_state(struct cn10k_rng *rng)
     35{
     36	struct arm_smccc_res res;
     37
     38	/* Send SMC service call to reset EBG health state */
     39	arm_smccc_smc(PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE, 0, 0, 0, 0, 0, 0, 0, &res);
     40	return res.a0;
     41}
     42
     43static int check_rng_health(struct cn10k_rng *rng)
     44{
     45	u64 status;
     46	unsigned long err;
     47
     48	/* Skip checking health */
     49	if (!rng->reg_base)
     50		return -ENODEV;
     51
     52	status = readq(rng->reg_base + RNM_PF_EBG_HEALTH);
     53	if (status & BIT_ULL(20)) {
     54		err = reset_rng_health_state(rng);
     55		if (err) {
     56			dev_err(&rng->pdev->dev, "HWRNG: Health test failed (status=%llx)\n",
     57					status);
     58			dev_err(&rng->pdev->dev, "HWRNG: error during reset (error=%lx)\n",
     59					err);
     60			return -EIO;
     61		}
     62	}
     63	return 0;
     64}
     65
     66static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
     67{
     68	u64 upper, lower;
     69
     70	*value = readq(rng->reg_base + RNM_PF_RANDOM);
     71
     72	/* HW can run out of entropy if large amount random data is read in
     73	 * quick succession. Zeros may not be real random data from HW.
     74	 */
     75	if (!*value) {
     76		upper = readq(rng->reg_base + RNM_PF_RANDOM);
     77		lower = readq(rng->reg_base + RNM_PF_RANDOM);
     78		while (!(upper & 0x00000000FFFFFFFFULL))
     79			upper = readq(rng->reg_base + RNM_PF_RANDOM);
     80		while (!(lower & 0xFFFFFFFF00000000ULL))
     81			lower = readq(rng->reg_base + RNM_PF_RANDOM);
     82
     83		*value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
     84	}
     85}
     86
     87static int cn10k_rng_read(struct hwrng *hwrng, void *data,
     88			  size_t max, bool wait)
     89{
     90	struct cn10k_rng *rng = (struct cn10k_rng *)hwrng->priv;
     91	unsigned int size;
     92	u8 *pos = data;
     93	int err = 0;
     94	u64 value;
     95
     96	err = check_rng_health(rng);
     97	if (err)
     98		return err;
     99
    100	size = max;
    101
    102	while (size >= 8) {
    103		cn10k_read_trng(rng, &value);
    104
    105		*((u64 *)pos) = value;
    106		size -= 8;
    107		pos += 8;
    108	}
    109
    110	if (size > 0) {
    111		cn10k_read_trng(rng, &value);
    112
    113		while (size > 0) {
    114			*pos = (u8)value;
    115			value >>= 8;
    116			size--;
    117			pos++;
    118		}
    119	}
    120
    121	return max - size;
    122}
    123
    124static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
    125{
    126	struct	cn10k_rng *rng;
    127	int	err;
    128
    129	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
    130	if (!rng)
    131		return -ENOMEM;
    132
    133	rng->pdev = pdev;
    134	pci_set_drvdata(pdev, rng);
    135
    136	rng->reg_base = pcim_iomap(pdev, 0, 0);
    137	if (!rng->reg_base) {
    138		dev_err(&pdev->dev, "Error while mapping CSRs, exiting\n");
    139		return -ENOMEM;
    140	}
    141
    142	rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
    143				       "cn10k-rng-%s", dev_name(&pdev->dev));
    144	if (!rng->ops.name)
    145		return -ENOMEM;
    146
    147	rng->ops.read    = cn10k_rng_read;
    148	rng->ops.quality = 1000;
    149	rng->ops.priv = (unsigned long)rng;
    150
    151	reset_rng_health_state(rng);
    152
    153	err = devm_hwrng_register(&pdev->dev, &rng->ops);
    154	if (err) {
    155		dev_err(&pdev->dev, "Could not register hwrng device.\n");
    156		return err;
    157	}
    158
    159	return 0;
    160}
    161
    162static void cn10k_rng_remove(struct pci_dev *pdev)
    163{
    164	/* Nothing to do */
    165}
    166
    167static const struct pci_device_id cn10k_rng_id_table[] = {
    168	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
    169	{0,},
    170};
    171
    172MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
    173
    174static struct pci_driver cn10k_rng_driver = {
    175	.name		= "cn10k_rng",
    176	.id_table	= cn10k_rng_id_table,
    177	.probe		= cn10k_rng_probe,
    178	.remove		= cn10k_rng_remove,
    179};
    180
    181module_pci_driver(cn10k_rng_driver);
    182MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
    183MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
    184MODULE_LICENSE("GPL v2");