1/*	$OpenBSD: mdrandom.c,v 1.3 2020/06/19 15:00:45 naddy Exp $	*/
2
3/*
4 * Copyright (c) 2020 Theo de Raadt
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <machine/psl.h>
21#include <machine/specialreg.h>
22
23#include "libsa.h"
24
25int
26mdrandom(char *buf, size_t buflen)
27{
28	u_int eax, ebx, ecx, edx;
29	int i;
30
31	for (i = 0; i < buflen; i++) {
32		uint32_t hi, lo, acc;
33
34		__asm volatile("rdtsc" : "=d" (hi), "=a" (lo));
35		acc = hi ^ lo;
36		acc ^= acc >> 16;
37		acc ^= acc >>  8;
38		buf[i] ^= acc;
39	}
40
41	CPUID(1, eax, ebx, ecx, edx);
42	if (ecx & CPUIDECX_RDRAND) {
43		unsigned long rand;
44		int retries;
45		uint8_t valid;
46
47		for (i = 0; i < buflen / sizeof(rand); i++) {
48			retries = 10;
49			do {
50				__asm volatile(
51				    "rdrand	%0;"
52				    "setc	%1;"
53				    : "=r" (rand), "=qm" (valid));
54			} while (!valid && --retries > 0);
55			((unsigned long *)buf)[i] ^= rand;
56		}
57	}
58
59	CPUID(0, eax, ebx, ecx, edx);
60	if (eax >= 7) {
61		CPUID_LEAF(7, 0, eax, ebx, ecx, edx);
62		if (ebx & SEFF0EBX_RDSEED) {
63			unsigned long rand;
64			int retries;
65			uint8_t valid;
66
67			for (i = 0; i < buflen / sizeof(rand); i++) {
68				retries = 10;
69				do {
70					__asm volatile(
71					    "rdseed	%0;"
72					    "setc	%1;"
73					    : "=r" (rand), "=qm" (valid));
74				} while (!valid && --retries > 0);
75				((unsigned long *)buf)[i] ^= rand;
76			}
77		}
78	}
79	return (0);
80}
81