Path: blob/master/drivers/crypto/loongson/loongson-rng.c
29268 views
// SPDX-License-Identifier: GPL-2.01/* Copyright (c) 2019 HiSilicon Limited. */2/* Copyright (c) 2025 Loongson Technology Corporation Limited. */34#include <linux/crypto.h>5#include <linux/err.h>6#include <linux/hw_random.h>7#include <linux/io.h>8#include <linux/iopoll.h>9#include <linux/kernel.h>10#include <linux/list.h>11#include <linux/mfd/loongson-se.h>12#include <linux/module.h>13#include <linux/mutex.h>14#include <linux/platform_device.h>15#include <linux/random.h>16#include <crypto/internal/rng.h>1718#define SE_SEED_SIZE 321920struct loongson_rng_list {21struct mutex lock;22struct list_head list;23int registered;24};2526struct loongson_rng {27u32 used;28struct loongson_se_engine *engine;29struct list_head list;30struct mutex lock;31};3233struct loongson_rng_ctx {34struct loongson_rng *rng;35};3637struct loongson_rng_cmd {38u32 cmd_id;39union {40u32 len;41u32 ret;42} u;43u32 seed_off;44u32 out_off;45u32 pad[4];46};4748static struct loongson_rng_list rng_devices = {49.lock = __MUTEX_INITIALIZER(rng_devices.lock),50.list = LIST_HEAD_INIT(rng_devices.list),51};5253static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src,54unsigned int slen, u8 *dstn, unsigned int dlen)55{56struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm);57struct loongson_rng *rng = ctx->rng;58struct loongson_rng_cmd *cmd = rng->engine->command;59int err, len;6061mutex_lock(&rng->lock);62cmd->seed_off = 0;63do {64len = min(dlen, rng->engine->buffer_size);65cmd = rng->engine->command;66cmd->u.len = len;67err = loongson_se_send_engine_cmd(rng->engine);68if (err)69break;7071cmd = rng->engine->command_ret;72if (cmd->u.ret) {73err = -EIO;74break;75}7677memcpy(dstn, rng->engine->data_buffer, len);78dlen -= len;79dstn += len;80} while (dlen > 0);81mutex_unlock(&rng->lock);8283return err;84}8586static int loongson_rng_init(struct crypto_tfm *tfm)87{88struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm);89struct loongson_rng *rng;90u32 min_used = U32_MAX;9192mutex_lock(&rng_devices.lock);93list_for_each_entry(rng, &rng_devices.list, list) {94if (rng->used < min_used) {95ctx->rng = rng;96min_used = rng->used;97}98}99ctx->rng->used++;100mutex_unlock(&rng_devices.lock);101102return 0;103}104105static void loongson_rng_exit(struct crypto_tfm *tfm)106{107struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm);108109mutex_lock(&rng_devices.lock);110ctx->rng->used--;111mutex_unlock(&rng_devices.lock);112}113114static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed,115unsigned int slen)116{117struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm);118struct loongson_rng *rng = ctx->rng;119struct loongson_rng_cmd *cmd;120int err;121122if (slen < SE_SEED_SIZE)123return -EINVAL;124125slen = min(slen, rng->engine->buffer_size);126127mutex_lock(&rng->lock);128cmd = rng->engine->command;129cmd->u.len = slen;130cmd->seed_off = rng->engine->buffer_off;131memcpy(rng->engine->data_buffer, seed, slen);132err = loongson_se_send_engine_cmd(rng->engine);133if (err)134goto out;135136cmd = rng->engine->command_ret;137if (cmd->u.ret)138err = -EIO;139out:140mutex_unlock(&rng->lock);141142return err;143}144145static struct rng_alg loongson_rng_alg = {146.generate = loongson_rng_generate,147.seed = loongson_rng_seed,148.seedsize = SE_SEED_SIZE,149.base = {150.cra_name = "stdrng",151.cra_driver_name = "loongson_stdrng",152.cra_priority = 300,153.cra_ctxsize = sizeof(struct loongson_rng_ctx),154.cra_module = THIS_MODULE,155.cra_init = loongson_rng_init,156.cra_exit = loongson_rng_exit,157},158};159160static int loongson_rng_probe(struct platform_device *pdev)161{162struct loongson_rng_cmd *cmd;163struct loongson_rng *rng;164int ret = 0;165166rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);167if (!rng)168return -ENOMEM;169170rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG);171if (!rng->engine)172return -ENODEV;173cmd = rng->engine->command;174cmd->cmd_id = SE_CMD_RNG;175cmd->out_off = rng->engine->buffer_off;176mutex_init(&rng->lock);177178mutex_lock(&rng_devices.lock);179180if (!rng_devices.registered) {181ret = crypto_register_rng(&loongson_rng_alg);182if (ret) {183dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret);184goto out;185}186rng_devices.registered = 1;187}188189list_add_tail(&rng->list, &rng_devices.list);190out:191mutex_unlock(&rng_devices.lock);192193return ret;194}195196static struct platform_driver loongson_rng_driver = {197.probe = loongson_rng_probe,198.driver = {199.name = "loongson-rng",200},201};202module_platform_driver(loongson_rng_driver);203204MODULE_ALIAS("platform:loongson-rng");205MODULE_LICENSE("GPL");206MODULE_AUTHOR("Yinggang Gu <[email protected]>");207MODULE_AUTHOR("Qunqin Zhao <[email protected]>");208MODULE_DESCRIPTION("Loongson Random Number Generator driver");209210211