Commit e623afb4 authored by Weili Qian's avatar Weili Qian
Browse files

crypto: hisilicon/trng - support to obtain random numbers from soft algorithm

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IBATCK


CVE: NA

----------------------------------------------------------------------

When trng device is busy and fails to apply for tfm, the driver applies for
software computed tfm to prevent task failures.

Signed-off-by: default avatarWeili Qian <qianweili@huawei.com>
Signed-off-by: default avatarJiangShui Yang <yangjiangshui@h-partners.com>
parent 9112ff5e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -80,5 +80,6 @@ config CRYPTO_DEV_HISI_TRNG
	depends on ARM64 && ACPI
	select HW_RANDOM
	select CRYPTO_RNG
	select CRYPTO_DRBG_CTR
	help
	  Support for HiSilicon TRNG Driver.
+47 −5
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
#define SEED_SHIFT_24		24
#define SEED_SHIFT_16		16
#define SEED_SHIFT_8		8
#define WAIT_PERIOD		20

struct hisi_trng_list {
	struct mutex lock;
@@ -53,6 +54,7 @@ struct hisi_trng {
	struct hisi_trng_list *trng_list;
	struct list_head list;
	struct hwrng rng;
	u32 ctx_num;
	u32 ver;
	bool is_used;
	struct mutex mutex;
@@ -60,6 +62,7 @@ struct hisi_trng {

struct hisi_trng_ctx {
	struct hisi_trng *trng;
	struct crypto_rng *drbg;
};

static atomic_t trng_active_devs;
@@ -85,6 +88,7 @@ static int hisi_trng_seed(struct crypto_rng *tfm, const u8 *seed,
			  unsigned int slen)
{
	struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm);
	struct crypto_rng *drbg = ctx->drbg;
	struct hisi_trng *trng = ctx->trng;
	u32 val = 0;
	int ret = 0;
@@ -95,6 +99,12 @@ static int hisi_trng_seed(struct crypto_rng *tfm, const u8 *seed,
		return -EINVAL;
	}

	/* when the device is busy, use soft tfm instead */
	if (drbg) {
		crypto_rng_set_entropy(drbg, seed, slen);
		return crypto_rng_reset(drbg, NULL, 0);
	}

	writel(0x0, trng->base + SW_DRBG_BLOCKS);
	hisi_trng_set_seed(trng, seed);

@@ -114,6 +124,7 @@ static int hisi_trng_generate(struct crypto_rng *tfm, const u8 *src,
			      unsigned int slen, u8 *dstn, unsigned int dlen)
{
	struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm);
	struct crypto_rng *drbg = ctx->drbg;
	struct hisi_trng *trng = ctx->trng;
	u32 data[SW_DRBG_DATA_NUM];
	u32 currsize = 0;
@@ -127,6 +138,9 @@ static int hisi_trng_generate(struct crypto_rng *tfm, const u8 *src,
		return -EINVAL;
	}

	if (drbg)
		return crypto_rng_generate(drbg, src, slen, dstn, dlen);

	do {
		ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS,
		     val, val & BIT(1), SLEEP_US, TIMEOUT_US);
@@ -156,19 +170,41 @@ static int hisi_trng_init(struct crypto_tfm *tfm)
{
	struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm);
	struct hisi_trng *trng;
	int ret = -EBUSY;
	int ret = 0;

	mutex_lock(&trng_devices.lock);
	list_for_each_entry(trng, &trng_devices.list, list) {
		if (!trng->is_used) {
			trng->is_used = true;
			trng->ctx_num++;
			ctx->trng = trng;
			ret = 0;
			break;
		}
	}
	mutex_unlock(&trng_devices.lock);

	if (!ctx->trng) {
		ctx->drbg = crypto_alloc_rng("drbg_nopr_ctr_aes256", 0, 0);
		if (IS_ERR(ctx->drbg)) {
			pr_err("Can not alloc rng!\n");
			ret = PTR_ERR(ctx->drbg);
			return ret;
		}

		mutex_lock(&trng_devices.lock);
		if (list_empty(&trng_devices.list)) {
			mutex_unlock(&trng_devices.lock);
			crypto_free_rng(ctx->drbg);
			return -ENODEV;
		}

		trng = list_first_entry(&trng_devices.list,
					struct hisi_trng, list);
		trng->ctx_num++;
		ctx->trng = trng;
		mutex_unlock(&trng_devices.lock);
	}

	return ret;
}

@@ -177,7 +213,12 @@ static void hisi_trng_exit(struct crypto_tfm *tfm)
	struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm);

	mutex_lock(&trng_devices.lock);
	if (!ctx->drbg)
		ctx->trng->is_used = false;
	else
		crypto_free_rng(ctx->drbg);

	ctx->trng->ctx_num--;
	mutex_unlock(&trng_devices.lock);
}

@@ -239,7 +280,7 @@ static int hisi_trng_del_from_list(struct hisi_trng *trng)
	int ret = -EBUSY;

	mutex_lock(&trng_devices.lock);
	if (!trng->is_used) {
	if (!trng->ctx_num) {
		list_del(&trng->list);
		ret = 0;
	}
@@ -264,6 +305,7 @@ static int hisi_trng_probe(struct platform_device *pdev)
		return PTR_ERR(trng->base);

	trng->is_used = false;
	trng->ctx_num = 0;
	trng->ver = readl(trng->base + HISI_TRNG_VERSION);
	if (!trng_devices.is_init) {
		INIT_LIST_HEAD(&trng_devices.list);
@@ -310,7 +352,7 @@ static void hisi_trng_remove(struct platform_device *pdev)

	/* Wait until the task is finished */
	while (hisi_trng_del_from_list(trng))
		;
		msleep(WAIT_PERIOD);

	if (trng->ver != HISI_TRNG_VER_V1 &&
	    atomic_dec_return(&trng_active_devs) == 0)