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