1240135Skib/*-
2256670Skib * Copyright (c) 2013 The FreeBSD Foundation
3254147Sobrien * Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org>
4240135Skib * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org>
5240135Skib * All rights reserved.
6240135Skib *
7256670Skib * Portions of this software were developed by Konstantin Belousov
8256670Skib * under sponsorship from the FreeBSD Foundation.
9256670Skib *
10240135Skib * Redistribution and use in source and binary forms, with or without
11240135Skib * modification, are permitted provided that the following conditions
12240135Skib * are met:
13240135Skib * 1. Redistributions of source code must retain the above copyright
14240135Skib *    notice, this list of conditions and the following disclaimer
15240135Skib *    in this position and unchanged.
16240135Skib * 2. Redistributions in binary form must reproduce the above copyright
17240135Skib *    notice, this list of conditions and the following disclaimer in the
18240135Skib *    documentation and/or other materials provided with the distribution.
19240135Skib *
20240135Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21240135Skib * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22240135Skib * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23240135Skib * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24240135Skib * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25240135Skib * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26240135Skib * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27240135Skib * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28240135Skib * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29240135Skib * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30240135Skib *
31240135Skib */
32240135Skib
33240135Skib#include <sys/cdefs.h>
34240135Skib__FBSDID("$FreeBSD$");
35240135Skib
36240135Skib#include <sys/param.h>
37254147Sobrien#include <sys/kernel.h>
38273872Smarkm#include <sys/conf.h>
39240135Skib#include <sys/lock.h>
40256377Smarkm#include <sys/malloc.h>
41254147Sobrien#include <sys/module.h>
42256377Smarkm#include <sys/random.h>
43240135Skib#include <sys/systm.h>
44254147Sobrien
45254147Sobrien#include <machine/md_var.h>
46254147Sobrien#include <machine/specialreg.h>
47254147Sobrien
48256377Smarkm#include <dev/random/randomdev.h>
49240135Skib
50240135Skib#define	RETRY_COUNT	10
51240135Skib
52273872Smarkmstatic u_int random_ivy_read(void *, u_int);
53240135Skib
54284959Smarkmstatic struct random_source random_ivy = {
55284959Smarkm	.rs_ident = "Intel Secure Key RNG",
56284959Smarkm	.rs_source = RANDOM_PURE_RDRAND,
57284959Smarkm	.rs_read = random_ivy_read
58240135Skib};
59240135Skib
60240135Skibstatic inline int
61274250Skibivy_rng_store(u_long *buf)
62240135Skib{
63240135Skib#ifdef __GNUCLIKE_ASM
64274250Skib	u_long rndval;
65256670Skib	int retry;
66240135Skib
67256670Skib	retry = RETRY_COUNT;
68240135Skib	__asm __volatile(
69256670Skib	    "1:\n\t"
70274381Skib	    "rdrand	%1\n\t"	/* read randomness into rndval */
71274250Skib	    "jc		2f\n\t" /* CF is set on success, exit retry loop */
72256670Skib	    "dec	%0\n\t" /* otherwise, retry-- */
73256670Skib	    "jne	1b\n\t" /* and loop if retries are not exhausted */
74274250Skib	    "2:"
75274250Skib	    : "+r" (retry), "=r" (rndval) : : "cc");
76274250Skib	*buf = rndval;
77256670Skib	return (retry);
78240135Skib#else /* __GNUCLIKE_ASM */
79240135Skib	return (0);
80240135Skib#endif
81240135Skib}
82240135Skib
83274250Skib/* It is required that buf length is a multiple of sizeof(u_long). */
84273872Smarkmstatic u_int
85273872Smarkmrandom_ivy_read(void *buf, u_int c)
86240135Skib{
87274250Skib	u_long *b, rndval;
88273872Smarkm	u_int count;
89240135Skib
90273872Smarkm	KASSERT(c % sizeof(*b) == 0, ("partial read %d", c));
91273872Smarkm	b = buf;
92273872Smarkm	for (count = c; count > 0; count -= sizeof(*b)) {
93274250Skib		if (ivy_rng_store(&rndval) == 0)
94240135Skib			break;
95274250Skib		*b++ = rndval;
96240135Skib	}
97240135Skib	return (c - count);
98240135Skib}
99240135Skib
100254147Sobrienstatic int
101254147Sobrienrdrand_modevent(module_t mod, int type, void *unused)
102254147Sobrien{
103256377Smarkm	int error = 0;
104254147Sobrien
105254147Sobrien	switch (type) {
106254147Sobrien	case MOD_LOAD:
107273872Smarkm		if (cpu_feature2 & CPUID2_RDRAND) {
108284959Smarkm			random_source_register(&random_ivy);
109284959Smarkm			printf("random: fast provider: \"%s\"\n", random_ivy.rs_ident);
110273872Smarkm		}
111256377Smarkm		break;
112256377Smarkm
113256377Smarkm	case MOD_UNLOAD:
114256377Smarkm		if (cpu_feature2 & CPUID2_RDRAND)
115284959Smarkm			random_source_deregister(&random_ivy);
116256377Smarkm		break;
117256377Smarkm
118256377Smarkm	case MOD_SHUTDOWN:
119256377Smarkm		break;
120256377Smarkm
121256377Smarkm	default:
122256377Smarkm		error = EOPNOTSUPP;
123256377Smarkm		break;
124256377Smarkm
125254147Sobrien	}
126254147Sobrien
127256377Smarkm	return (error);
128254147Sobrien}
129254147Sobrien
130273872SmarkmDEV_MODULE(rdrand, rdrand_modevent, NULL);
131273872SmarkmMODULE_VERSION(rdrand, 1);
132298102SkibMODULE_DEPEND(rdrand, random_device, 1, 1, 1);
133