xref: /openbsd/sys/arch/amd64/stand/libsa/mdrandom.c (revision 76d0caae)
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 
25 int
26 mdrandom(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