1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2016 Linaro Ltd; <ard.biesheuvel@linaro.org> 4 */ 5 6 #include <linux/efi.h> 7 #include <asm/efi.h> 8 9 #include "efistub.h" 10 11 typedef union efi_rng_protocol efi_rng_protocol_t; 12 13 union efi_rng_protocol { 14 struct { 15 efi_status_t (__efiapi *get_info)(efi_rng_protocol_t *, 16 unsigned long *, 17 efi_guid_t *); 18 efi_status_t (__efiapi *get_rng)(efi_rng_protocol_t *, 19 efi_guid_t *, unsigned long, 20 u8 *out); 21 }; 22 struct { 23 u32 get_info; 24 u32 get_rng; 25 } mixed_mode; 26 }; 27 28 /** 29 * efi_get_random_bytes() - fill a buffer with random bytes 30 * @size: size of the buffer 31 * @out: caller allocated buffer to receive the random bytes 32 * 33 * The call will fail if either the firmware does not implement the 34 * EFI_RNG_PROTOCOL or there are not enough random bytes available to fill 35 * the buffer. 36 * 37 * Return: status code 38 */ 39 efi_status_t efi_get_random_bytes(unsigned long size, u8 *out) 40 { 41 efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; 42 efi_status_t status; 43 efi_rng_protocol_t *rng = NULL; 44 45 status = efi_bs_call(locate_protocol, &rng_proto, NULL, (void **)&rng); 46 if (status != EFI_SUCCESS) 47 return status; 48 49 return efi_call_proto(rng, get_rng, NULL, size, out); 50 } 51 52 /** 53 * efi_random_get_seed() - provide random seed as configuration table 54 * 55 * The EFI_RNG_PROTOCOL is used to read random bytes. These random bytes are 56 * saved as a configuration table which can be used as entropy by the kernel 57 * for the initialization of its pseudo random number generator. 58 * 59 * If the EFI_RNG_PROTOCOL is not available or there are not enough random bytes 60 * available, the configuration table will not be installed and an error code 61 * will be returned. 62 * 63 * Return: status code 64 */ 65 efi_status_t efi_random_get_seed(void) 66 { 67 efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; 68 efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW; 69 efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID; 70 struct linux_efi_random_seed *prev_seed, *seed = NULL; 71 int prev_seed_size = 0, seed_size = EFI_RANDOM_SEED_SIZE; 72 efi_rng_protocol_t *rng = NULL; 73 efi_status_t status; 74 75 status = efi_bs_call(locate_protocol, &rng_proto, NULL, (void **)&rng); 76 if (status != EFI_SUCCESS) 77 return status; 78 79 /* 80 * Check whether a seed was provided by a prior boot stage. In that 81 * case, instead of overwriting it, let's create a new buffer that can 82 * hold both, and concatenate the existing and the new seeds. 83 * Note that we should read the seed size with caution, in case the 84 * table got corrupted in memory somehow. 85 */ 86 prev_seed = get_efi_config_table(LINUX_EFI_RANDOM_SEED_TABLE_GUID); 87 if (prev_seed && prev_seed->size <= 512U) { 88 prev_seed_size = prev_seed->size; 89 seed_size += prev_seed_size; 90 } 91 92 /* 93 * Use EFI_ACPI_RECLAIM_MEMORY here so that it is guaranteed that the 94 * allocation will survive a kexec reboot (although we refresh the seed 95 * beforehand) 96 */ 97 status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, 98 struct_size(seed, bits, seed_size), 99 (void **)&seed); 100 if (status != EFI_SUCCESS) { 101 efi_warn("Failed to allocate memory for RNG seed.\n"); 102 goto err_warn; 103 } 104 105 status = efi_call_proto(rng, get_rng, &rng_algo_raw, 106 EFI_RANDOM_SEED_SIZE, seed->bits); 107 108 if (status == EFI_UNSUPPORTED) 109 /* 110 * Use whatever algorithm we have available if the raw algorithm 111 * is not implemented. 112 */ 113 status = efi_call_proto(rng, get_rng, NULL, 114 EFI_RANDOM_SEED_SIZE, seed->bits); 115 116 if (status != EFI_SUCCESS) 117 goto err_freepool; 118 119 seed->size = seed_size; 120 if (prev_seed_size) 121 memcpy(seed->bits + EFI_RANDOM_SEED_SIZE, prev_seed->bits, 122 prev_seed_size); 123 124 status = efi_bs_call(install_configuration_table, &rng_table_guid, seed); 125 if (status != EFI_SUCCESS) 126 goto err_freepool; 127 128 if (prev_seed_size) { 129 /* wipe and free the old seed if we managed to install the new one */ 130 memzero_explicit(prev_seed->bits, prev_seed_size); 131 efi_bs_call(free_pool, prev_seed); 132 } 133 return EFI_SUCCESS; 134 135 err_freepool: 136 memzero_explicit(seed, struct_size(seed, bits, seed_size)); 137 efi_bs_call(free_pool, seed); 138 efi_warn("Failed to obtain seed from EFI_RNG_PROTOCOL\n"); 139 err_warn: 140 if (prev_seed) 141 efi_warn("Retaining bootloader-supplied seed only"); 142 return status; 143 } 144