Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Marvell CN10K RVU Hardware Random Number Generator.
0003  *
0004  * Copyright (C) 2021 Marvell.
0005  *
0006  */
0007 
0008 #include <linux/hw_random.h>
0009 #include <linux/io.h>
0010 #include <linux/module.h>
0011 #include <linux/pci.h>
0012 #include <linux/pci_ids.h>
0013 #include <linux/delay.h>
0014 
0015 #include <linux/arm-smccc.h>
0016 
0017 /* CSRs */
0018 #define RNM_CTL_STATUS      0x000
0019 #define RNM_ENTROPY_STATUS  0x008
0020 #define RNM_CONST       0x030
0021 #define RNM_EBG_ENT     0x048
0022 #define RNM_PF_EBG_HEALTH   0x050
0023 #define RNM_PF_RANDOM       0x400
0024 #define RNM_TRNG_RESULT     0x408
0025 
0026 struct cn10k_rng {
0027     void __iomem *reg_base;
0028     struct hwrng ops;
0029     struct pci_dev *pdev;
0030 };
0031 
0032 #define PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE     0xc2000b0f
0033 
0034 static unsigned long reset_rng_health_state(struct cn10k_rng *rng)
0035 {
0036     struct arm_smccc_res res;
0037 
0038     /* Send SMC service call to reset EBG health state */
0039     arm_smccc_smc(PLAT_OCTEONTX_RESET_RNG_EBG_HEALTH_STATE, 0, 0, 0, 0, 0, 0, 0, &res);
0040     return res.a0;
0041 }
0042 
0043 static int check_rng_health(struct cn10k_rng *rng)
0044 {
0045     u64 status;
0046     unsigned long err;
0047 
0048     /* Skip checking health */
0049     if (!rng->reg_base)
0050         return -ENODEV;
0051 
0052     status = readq(rng->reg_base + RNM_PF_EBG_HEALTH);
0053     if (status & BIT_ULL(20)) {
0054         err = reset_rng_health_state(rng);
0055         if (err) {
0056             dev_err(&rng->pdev->dev, "HWRNG: Health test failed (status=%llx)\n",
0057                     status);
0058             dev_err(&rng->pdev->dev, "HWRNG: error during reset (error=%lx)\n",
0059                     err);
0060             return -EIO;
0061         }
0062     }
0063     return 0;
0064 }
0065 
0066 static void cn10k_read_trng(struct cn10k_rng *rng, u64 *value)
0067 {
0068     u64 upper, lower;
0069 
0070     *value = readq(rng->reg_base + RNM_PF_RANDOM);
0071 
0072     /* HW can run out of entropy if large amount random data is read in
0073      * quick succession. Zeros may not be real random data from HW.
0074      */
0075     if (!*value) {
0076         upper = readq(rng->reg_base + RNM_PF_RANDOM);
0077         lower = readq(rng->reg_base + RNM_PF_RANDOM);
0078         while (!(upper & 0x00000000FFFFFFFFULL))
0079             upper = readq(rng->reg_base + RNM_PF_RANDOM);
0080         while (!(lower & 0xFFFFFFFF00000000ULL))
0081             lower = readq(rng->reg_base + RNM_PF_RANDOM);
0082 
0083         *value = (upper & 0xFFFFFFFF00000000) | (lower & 0xFFFFFFFF);
0084     }
0085 }
0086 
0087 static int cn10k_rng_read(struct hwrng *hwrng, void *data,
0088               size_t max, bool wait)
0089 {
0090     struct cn10k_rng *rng = (struct cn10k_rng *)hwrng->priv;
0091     unsigned int size;
0092     u8 *pos = data;
0093     int err = 0;
0094     u64 value;
0095 
0096     err = check_rng_health(rng);
0097     if (err)
0098         return err;
0099 
0100     size = max;
0101 
0102     while (size >= 8) {
0103         cn10k_read_trng(rng, &value);
0104 
0105         *((u64 *)pos) = value;
0106         size -= 8;
0107         pos += 8;
0108     }
0109 
0110     if (size > 0) {
0111         cn10k_read_trng(rng, &value);
0112 
0113         while (size > 0) {
0114             *pos = (u8)value;
0115             value >>= 8;
0116             size--;
0117             pos++;
0118         }
0119     }
0120 
0121     return max - size;
0122 }
0123 
0124 static int cn10k_rng_probe(struct pci_dev *pdev, const struct pci_device_id *id)
0125 {
0126     struct  cn10k_rng *rng;
0127     int err;
0128 
0129     rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
0130     if (!rng)
0131         return -ENOMEM;
0132 
0133     rng->pdev = pdev;
0134     pci_set_drvdata(pdev, rng);
0135 
0136     rng->reg_base = pcim_iomap(pdev, 0, 0);
0137     if (!rng->reg_base) {
0138         dev_err(&pdev->dev, "Error while mapping CSRs, exiting\n");
0139         return -ENOMEM;
0140     }
0141 
0142     rng->ops.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
0143                        "cn10k-rng-%s", dev_name(&pdev->dev));
0144     if (!rng->ops.name)
0145         return -ENOMEM;
0146 
0147     rng->ops.read    = cn10k_rng_read;
0148     rng->ops.quality = 1000;
0149     rng->ops.priv = (unsigned long)rng;
0150 
0151     reset_rng_health_state(rng);
0152 
0153     err = devm_hwrng_register(&pdev->dev, &rng->ops);
0154     if (err) {
0155         dev_err(&pdev->dev, "Could not register hwrng device.\n");
0156         return err;
0157     }
0158 
0159     return 0;
0160 }
0161 
0162 static void cn10k_rng_remove(struct pci_dev *pdev)
0163 {
0164     /* Nothing to do */
0165 }
0166 
0167 static const struct pci_device_id cn10k_rng_id_table[] = {
0168     { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA098) }, /* RNG PF */
0169     {0,},
0170 };
0171 
0172 MODULE_DEVICE_TABLE(pci, cn10k_rng_id_table);
0173 
0174 static struct pci_driver cn10k_rng_driver = {
0175     .name       = "cn10k_rng",
0176     .id_table   = cn10k_rng_id_table,
0177     .probe      = cn10k_rng_probe,
0178     .remove     = cn10k_rng_remove,
0179 };
0180 
0181 module_pci_driver(cn10k_rng_driver);
0182 MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
0183 MODULE_DESCRIPTION("Marvell CN10K HW RNG Driver");
0184 MODULE_LICENSE("GPL v2");