[PATCH 4/5] rng driver update

Jeff Garzik (jgarzik@pobox.com)
Thu, 13 Mar 2003 19:04:56 -0500


This is a MIME-formatted message. If you see this text it means that your
E-mail software does not support MIME-formatted messages.

--=_courier-21963-1047600496-0001-2
Content-Type: text/plain; charset=iso-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

--=_courier-21963-1047600496-0001-2
Content-Type: text/plain; name="patch.4"; charset=iso-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="patch.4"

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
# ChangeSet 1.1107 -> 1.1108
# drivers/char/Kconfig 1.9 -> 1.10
# drivers/char/hw_random.c 1.5 -> 1.6
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/03/13 jgarzik@redhat.com 1.1108
# [hw_random] add support for VIA Nehemiah RNG ("xstore" instruction)
# --------------------------------------------
#
diff -Nru a/drivers/char/Kconfig b/drivers/char/Kconfig
--- a/drivers/char/Kconfig Thu Mar 13 18:53:05 2003
+++ b/drivers/char/Kconfig Thu Mar 13 18:53:05 2003
@@ -710,7 +710,7 @@
If you're not sure, say N.

config HW_RANDOM
- tristate "Intel/AMD H/W Random Number Generator support"
+ tristate "Intel/AMD/Via H/W Random Number Generator support"
depends on (X86 || IA64) && PCI
---help---
This driver provides kernel-side support for the Random Number
diff -Nru a/drivers/char/hw_random.c b/drivers/char/hw_random.c
--- a/drivers/char/hw_random.c Thu Mar 13 18:53:05 2003
+++ b/drivers/char/hw_random.c Thu Mar 13 18:53:05 2003
@@ -1,5 +1,5 @@
/*
- Hardware driver for the Intel/AMD Random Number Generators (RNG)
+ Hardware driver for the Intel/AMD/Via Random Number Generators (RNG)
(c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>

derived from
@@ -35,6 +35,11 @@
#include <linux/mm.h>
#include <linux/delay.h>

+#ifdef __i386__
+#include <asm/msr.h>
+#include <asm/cpufeature.h>
+#endif
+
#include <asm/io.h>
#include <asm/uaccess.h>

@@ -88,6 +93,11 @@
static unsigned int amd_data_present (void);
static u32 amd_data_read (void);

+static int __init via_init(struct pci_dev *dev);
+static void __exit via_cleanup(void);
+static unsigned int via_data_present (void);
+static u32 via_data_read (void);
+
struct rng_operations {
int (*init) (struct pci_dev *dev);
void (*cleanup) (void);
@@ -117,6 +127,7 @@
rng_hw_none,
rng_hw_intel,
rng_hw_amd,
+ rng_hw_via,
};

static struct rng_operations rng_vendor_ops[] __initdata = {
@@ -129,6 +140,9 @@

/* rng_hw_amd */
{ amd_init, amd_cleanup, amd_data_present, amd_data_read, 4 },
+
+ /* rng_hw_via */
+ { via_init, via_cleanup, via_data_present, via_data_read, 1 },
};

/*
@@ -332,6 +346,127 @@

/***********************************************************************
*
+ * Via RNG operations
+ *
+ */
+
+enum {
+ VIA_STRFILT_CNT_SHIFT = 16,
+ VIA_STRFILT_FAIL = (1 << 15),
+ VIA_STRFILT_ENABLE = (1 << 14),
+ VIA_RAWBITS_ENABLE = (1 << 13),
+ VIA_RNG_ENABLE = (1 << 6),
+ VIA_XSTORE_CNT_MASK = 0x0F,
+
+ VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */
+ VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */
+ VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF,
+ VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */
+ VIA_RNG_CHUNK_2_MASK = 0xFFFF,
+ VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */
+ VIA_RNG_CHUNK_1_MASK = 0xFF,
+};
+
+u32 via_rng_datum;
+
+/*
+ * Investigate using the 'rep' prefix to obtain 32 bits of random data
+ * in one insn. The upside is potentially better performance. The
+ * downside is that the instruction becomes no longer atomic. Due to
+ * this, just like familiar issues with /dev/random itself, the worst
+ * case of a 'rep xstore' could potentially pause a cpu for an
+ * unreasonably long time. In practice, this condition would likely
+ * only occur when the hardware is failing. (or so we hope :))
+ *
+ * Another possible performance boost may come from simply buffering
+ * until we have 4 bytes, thus returning a u32 at a time,
+ * instead of the current u8-at-a-time.
+ */
+
+static inline u32 xstore(u32 *addr, u32 edx_in)
+{
+ u32 eax_out;
+
+ asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */"
+ :"=m"(*addr), "=a"(eax_out)
+ :"D"(addr), "d"(edx_in));
+
+ return eax_out;
+}
+
+static unsigned int via_data_present(void)
+{
+ u32 bytes_out;
+
+ /* We choose the recommended 1-byte-per-instruction RNG rate,
+ * for greater randomness at the expense of speed. Larger
+ * values 2, 4, or 8 bytes-per-instruction yield greater
+ * speed at lesser randomness.
+ *
+ * If you change this to another VIA_CHUNK_n, you must also
+ * change the ->n_bytes values in rng_vendor_ops[] tables.
+ * VIA_CHUNK_8 requires further code changes.
+ *
+ * A copy of MSR_VIA_RNG is placed in eax_out when xstore
+ * completes.
+ */
+ via_rng_datum = 0; /* paranoia, not really necessary */
+ bytes_out = xstore(&via_rng_datum, VIA_RNG_CHUNK_1) & VIA_XSTORE_CNT_MASK;
+ if (bytes_out == 0)
+ return 0;
+
+ return 1;
+}
+
+static u32 via_data_read(void)
+{
+ return via_rng_datum;
+}
+
+static int __init via_init(struct pci_dev *dev)
+{
+ u32 lo, hi, old_lo;
+
+ /* Control the RNG via MSR. Tread lightly and pay very close
+ * close attention to values written, as the reserved fields
+ * are documented to be "undefined and unpredictable"; but it
+ * does not say to write them as zero, so I make a guess that
+ * we restore the values we find in the register.
+ */
+ rdmsr(MSR_VIA_RNG, lo, hi);
+
+ old_lo = lo;
+ lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT);
+ lo &= ~VIA_XSTORE_CNT_MASK;
+ lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE);
+ lo |= VIA_RNG_ENABLE;
+
+ if (lo != old_lo)
+ wrmsr(MSR_VIA_RNG, lo, hi);
+
+ /* perhaps-unnecessary sanity check; remove after testing if
+ unneeded */
+ rdmsr(MSR_VIA_RNG, lo, hi);
+ if ((lo & VIA_RNG_ENABLE) == 0) {
+ printk(KERN_ERR PFX "cannot enable Via C3 RNG, aborting\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit via_cleanup(void)
+{
+ u32 lo, hi;
+
+ rdmsr(MSR_VIA_RNG, lo, hi);
+ lo &= ~VIA_RNG_ENABLE;
+ wrmsr(MSR_VIA_RNG, lo, hi);
+}
+
+
+/***********************************************************************
+ *
* /dev/hwrandom character device handling (major 10, minor 183)
*
*/
@@ -470,6 +605,15 @@
goto match;
}
}
+
+#ifdef __i386__
+ /* Probe for Via RNG */
+ if (cpu_has_xstore) {
+ rng_ops = &rng_vendor_ops[rng_hw_via];
+ pdev = NULL;
+ goto match;
+ }
+#endif

DPRINTK ("EXIT, returning -ENODEV\n");
return -ENODEV;

--=_courier-21963-1047600496-0001-2--