xref: /freebsd/sys/dev/random/ivy.c (revision 30b318b92f13d33f63270b76fba2c32d8668ab7a)
1 /*-
2  * Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org>
3  * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer
11  *    in this position and unchanged.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/malloc.h>
36 #include <sys/module.h>
37 #include <sys/random.h>
38 #include <sys/selinfo.h>
39 #include <sys/systm.h>
40 
41 #include <machine/md_var.h>
42 #include <machine/specialreg.h>
43 
44 #include <dev/random/randomdev.h>
45 #include <dev/random/randomdev_soft.h>
46 #include <dev/random/random_harvestq.h>
47 #include <dev/random/live_entropy_sources.h>
48 #include <dev/random/random_adaptors.h>
49 
50 #define	RETRY_COUNT	10
51 
52 static int random_ivy_read(void *, int);
53 
54 static struct random_hardware_source random_ivy = {
55 	.ident = "Hardware, Intel IvyBridge+ RNG",
56 	.source = RANDOM_PURE_RDRAND,
57 	.read = random_ivy_read
58 };
59 
60 static inline int
61 ivy_rng_store(uint64_t *tmp)
62 {
63 #ifdef __GNUCLIKE_ASM
64 	uint32_t count;
65 
66 	__asm __volatile(
67 #ifdef __amd64__
68 	    "rdrand\t%%rax\n\t"
69 	    "jnc\t1f\n\t"
70 	    "movq\t%%rax,%1\n\t"
71 	    "movl\t$8,%%eax\n"
72 #else /* i386 */
73 	    "rdrand\t%%eax\n\t"
74 	    "jnc\t1f\n\t"
75 	    "movl\t%%eax,%1\n\t"
76 	    "movl\t$4,%%eax\n"
77 #endif
78 	    "1:\n"	/* %eax is cleared by processor on failure */
79 	    : "=a" (count), "=g" (*tmp) : "a" (0) : "cc");
80 	return (count);
81 #else /* __GNUCLIKE_ASM */
82 	return (0);
83 #endif
84 }
85 
86 static int
87 random_ivy_read(void *buf, int c)
88 {
89 	uint8_t *b;
90 	int count, ret, retry;
91 	uint64_t tmp;
92 
93 	b = buf;
94 	for (count = c; count > 0; count -= ret) {
95 		for (retry = 0; retry < RETRY_COUNT; retry++) {
96 			ret = ivy_rng_store(&tmp);
97 			if (ret != 0)
98 				break;
99 		}
100 		if (ret == 0)
101 			break;
102 		if (ret > count)
103 			ret = count;
104 		memcpy(b, &tmp, ret);
105 		b += ret;
106 	}
107 	return (c - count);
108 }
109 
110 static int
111 rdrand_modevent(module_t mod, int type, void *unused)
112 {
113 	int error = 0;
114 
115 	switch (type) {
116 	case MOD_LOAD:
117 		if (cpu_feature2 & CPUID2_RDRAND)
118 			live_entropy_source_register(&random_ivy);
119 		else
120 #ifndef KLD_MODULE
121 			if (bootverbose)
122 #endif
123 				printf("%s: RDRAND is not present\n",
124 				    random_ivy.ident);
125 		break;
126 
127 	case MOD_UNLOAD:
128 		if (cpu_feature2 & CPUID2_RDRAND)
129 			live_entropy_source_deregister(&random_ivy);
130 		break;
131 
132 	case MOD_SHUTDOWN:
133 		break;
134 
135 	default:
136 		error = EOPNOTSUPP;
137 		break;
138 
139 	}
140 
141 	return (error);
142 }
143 
144 LIVE_ENTROPY_SRC_MODULE(random_rdrand, rdrand_modevent, 1);
145