xref: /openbsd-src/sys/arch/i386/stand/libsa/mdrandom.c (revision 54b0a309fb3f2a8ec180dc311cfa629a40bad568)
1 /*	$OpenBSD: mdrandom.c,v 1.4 2024/09/26 10:12:02 jsg 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/specialreg.h>
21 
22 #include "libsa.h"
23 
24 int
25 mdrandom(char *buf, size_t buflen)
26 {
27 	u_int eax, ebx, ecx, edx;
28 	int ret = -1;
29 	int i;
30 
31 	if (pslid() == 0)
32 		goto done;
33 	CPUID(1, eax, ebx, ecx, edx);
34 	if (edx & CPUID_TSC) {
35 		uint32_t hi, lo, acc;
36 
37 		for (i = 0; i < buflen; i++) {
38 			__asm volatile("rdtsc" : "=d" (hi), "=a" (lo));
39 			acc = hi ^ lo;
40 			acc ^= acc >> 16;
41 			acc ^= acc >>  8;
42 			buf[i] ^= acc;
43 		}
44 		ret = 0;
45 	}
46 	if (ecx & CPUIDECX_RDRAND) {
47 		unsigned long rand;
48 		int retries;
49 		uint8_t valid;
50 
51 		for (i = 0; i < buflen / sizeof(rand); i++) {
52 			retries = 10;
53 			do {
54 				__asm volatile(
55 				    "rdrand	%0;"
56 				    "setc	%1;"
57 				    : "=r" (rand), "=qm" (valid));
58 			} while (!valid && --retries > 0);
59 			((unsigned long *)buf)[i] ^= rand;
60 		}
61 		ret = 0;
62 	}
63 	CPUID(0, eax, ebx, ecx, edx);
64 	if (eax >= 7) {
65 		CPUID_LEAF(7, 0, eax, ebx, ecx, edx);
66 		if (ebx & SEFF0EBX_RDSEED) {
67 			unsigned long rand;
68 			int retries;
69 			uint8_t valid;
70 
71 			for (i = 0; i < buflen / sizeof(rand); i++) {
72 				retries = 10;
73 				do {
74 					__asm volatile(
75 					    "rdseed	%0;"
76 					    "setc	%1;"
77 					    : "=r" (rand), "=qm" (valid));
78 				} while (!valid && --retries > 0);
79 				((unsigned long *)buf)[i] ^= rand;
80 			}
81 			ret = 0;
82 		}
83 	}
84 done:
85 	return ret;
86 }
87