Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 00044f17 authored by Christian Lamparter's avatar Christian Lamparter Committed by John W. Linville
Browse files

carl9170: export HW random number generator



All AR9170 hardware have a 16-Bit random number generator.
The documentation claims the values are suitable for
"security keys".

The "throughput" is around 320Kibit/s. It's slow, but it
does work without introducing any special offload
firmware commands.

Signed-off-by: default avatarChristian Lamparter <chunkeey@googlemail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent acf17712
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -39,3 +39,17 @@ config CARL9170_WPC
	bool
	depends on CARL9170 && (INPUT = y || INPUT = CARL9170)
	default y

config CARL9170_HWRNG
        bool "Random number generator"
        depends on CARL9170 && (HW_RANDOM = y || HW_RANDOM = CARL9170)
        default n
	help
	  Provides a hardware random number generator to the kernel.

	  SECURITY WARNING: It's relatively easy to eavesdrop all
	  generated random numbers from the transport stream with
	  usbmon [software] or special usb sniffer hardware.

	  Say N, unless your setup[i.e.: embedded system] has no
	  other rng source and you can afford to take the risk.
+12 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
#include <linux/firmware.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/hw_random.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <linux/usb.h>
@@ -449,6 +450,17 @@ struct ar9170 {
		unsigned int off_override;
		bool state;
	} ps;

#ifdef CONFIG_CARL9170_HWRNG
# define CARL9170_HWRNG_CACHE_SIZE	CARL9170_MAX_CMD_PAYLOAD_LEN
	struct {
		struct hwrng rng;
		bool initialized;
		char name[30 + 1];
		u16 cache[CARL9170_HWRNG_CACHE_SIZE / sizeof(u16)];
		unsigned int cache_idx;
	} rng;
#endif /* CONFIG_CARL9170_HWRNG */
};

enum carl9170_ps_off_override_reasons {
+113 −0
Original line number Diff line number Diff line
@@ -1468,6 +1468,109 @@ static int carl9170_register_wps_button(struct ar9170 *ar)
}
#endif /* CONFIG_CARL9170_WPC */

#ifdef CONFIG_CARL9170_HWRNG
static int carl9170_rng_get(struct ar9170 *ar)
{

#define RW	(CARL9170_MAX_CMD_PAYLOAD_LEN / sizeof(u32))
#define RB	(CARL9170_MAX_CMD_PAYLOAD_LEN)

	static const __le32 rng_load[RW] = {
		[0 ... (RW - 1)] = cpu_to_le32(AR9170_RAND_REG_NUM)};

	u32 buf[RW];

	unsigned int i, off = 0, transfer, count;
	int err;

	BUILD_BUG_ON(RB > CARL9170_MAX_CMD_PAYLOAD_LEN);

	if (!IS_ACCEPTING_CMD(ar) || !ar->rng.initialized)
		return -EAGAIN;

	count = ARRAY_SIZE(ar->rng.cache);
	while (count) {
		err = carl9170_exec_cmd(ar, CARL9170_CMD_RREG,
					RB, (u8 *) rng_load,
					RB, (u8 *) buf);
		if (err)
			return err;

		transfer = min_t(unsigned int, count, RW);
		for (i = 0; i < transfer; i++)
			ar->rng.cache[off + i] = buf[i];

		off += transfer;
		count -= transfer;
	}

	ar->rng.cache_idx = 0;

#undef RW
#undef RB
	return 0;
}

static int carl9170_rng_read(struct hwrng *rng, u32 *data)
{
	struct ar9170 *ar = (struct ar9170 *)rng->priv;
	int ret = -EIO;

	mutex_lock(&ar->mutex);
	if (ar->rng.cache_idx >= ARRAY_SIZE(ar->rng.cache)) {
		ret = carl9170_rng_get(ar);
		if (ret) {
			mutex_unlock(&ar->mutex);
			return ret;
		}
	}

	*data = ar->rng.cache[ar->rng.cache_idx++];
	mutex_unlock(&ar->mutex);

	return sizeof(u16);
}

static void carl9170_unregister_hwrng(struct ar9170 *ar)
{
	if (ar->rng.initialized) {
		hwrng_unregister(&ar->rng.rng);
		ar->rng.initialized = false;
	}
}

static int carl9170_register_hwrng(struct ar9170 *ar)
{
	int err;

	snprintf(ar->rng.name, ARRAY_SIZE(ar->rng.name),
		 "%s_%s", KBUILD_MODNAME, wiphy_name(ar->hw->wiphy));
	ar->rng.rng.name = ar->rng.name;
	ar->rng.rng.data_read = carl9170_rng_read;
	ar->rng.rng.priv = (unsigned long)ar;

	if (WARN_ON(ar->rng.initialized))
		return -EALREADY;

	err = hwrng_register(&ar->rng.rng);
	if (err) {
		dev_err(&ar->udev->dev, "Failed to register the random "
			"number generator (%d)\n", err);
		return err;
	}

	ar->rng.initialized = true;

	err = carl9170_rng_get(ar);
	if (err) {
		carl9170_unregister_hwrng(ar);
		return err;
	}

	return 0;
}
#endif /* CONFIG_CARL9170_HWRNG */

static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx,
				struct survey_info *survey)
{
@@ -1878,6 +1981,12 @@ int carl9170_register(struct ar9170 *ar)
		goto err_unreg;
#endif /* CONFIG_CARL9170_WPC */

#ifdef CONFIG_CARL9170_HWRNG
	err = carl9170_register_hwrng(ar);
	if (err)
		goto err_unreg;
#endif /* CONFIG_CARL9170_HWRNG */

	dev_info(&ar->udev->dev, "Atheros AR9170 is registered as '%s'\n",
		 wiphy_name(ar->hw->wiphy));

@@ -1910,6 +2019,10 @@ void carl9170_unregister(struct ar9170 *ar)
	}
#endif /* CONFIG_CARL9170_WPC */

#ifdef CONFIG_CARL9170_HWRNG
	carl9170_unregister_hwrng(ar);
#endif /* CONFIG_CARL9170_HWRNG */

	carl9170_cancel_worker(ar);
	cancel_work_sync(&ar->restart_work);