xref: /aosp_15_r20/external/coreboot/src/arch/x86/rdrand.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <random.h>
4 #include <types.h>
5 
6 /*
7  * Intel recommends that applications attempt 10 retries in a tight loop
8  * in the unlikely event that the RDRAND instruction does not successfully
9  * return a random number. The odds of ten failures in a row would in fact
10  * be an indication of a larger CPU issue.
11  */
12 #define RDRAND_RETRY_LOOPS 10
13 
14 /*
15  * Generate a 32-bit random number through RDRAND instruction.
16  * Carry flag is set on RDRAND success and 0 on failure.
17  */
rdrand_32(uint32_t * rand)18 static inline uint8_t rdrand_32(uint32_t *rand)
19 {
20 	uint8_t carry;
21 
22 	__asm__ __volatile__(
23 	     ".byte 0x0f; .byte 0xc7; .byte 0xf0; setc %1"
24 	     : "=a" (*rand), "=qm" (carry));
25 	return carry;
26 }
27 
28 #if ENV_X86_64
29 /*
30  * Generate a 64-bit random number through RDRAND instruction.
31  * Carry flag is set on RDRAND success and 0 on failure.
32  */
rdrand_64(uint64_t * rand)33 static inline uint8_t rdrand_64(uint64_t *rand)
34 {
35 	uint8_t carry;
36 
37 	__asm__ __volatile__(
38 	     ".byte 0x48; .byte 0x0f; .byte 0xc7; .byte 0xf0; setc %1"
39 	     : "=a" (*rand), "=qm" (carry));
40 	return carry;
41 }
42 #endif
43 
get_random_number_32(uint32_t * rand)44 enum cb_err get_random_number_32(uint32_t *rand)
45 {
46 	int i;
47 
48 	/* Perform a loop call until RDRAND succeeds or returns failure. */
49 	for (i = 0; i < RDRAND_RETRY_LOOPS; i++) {
50 		if (rdrand_32(rand))
51 			return CB_SUCCESS;
52 	}
53 	return CB_ERR;
54 }
55 
get_random_number_64(uint64_t * rand)56 enum cb_err get_random_number_64(uint64_t *rand)
57 {
58 	int i;
59 	uint32_t rand_high, rand_low;
60 
61 	/* Perform a loop call until RDRAND succeeds or returns failure. */
62 	for (i = 0; i < RDRAND_RETRY_LOOPS; i++) {
63 #if ENV_X86_64
64 		if (rdrand_64(rand))
65 			return CB_SUCCESS;
66 #endif
67 		if (rdrand_32(&rand_high) && rdrand_32(&rand_low)) {
68 			*rand = ((uint64_t)rand_high << 32) |
69 				(uint64_t)rand_low;
70 			return CB_SUCCESS;
71 		}
72 	}
73 	return CB_ERR;
74 }
75