| .. | .. |
|---|
| 67 | 67 | efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; |
|---|
| 68 | 68 | efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW; |
|---|
| 69 | 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; |
|---|
| 70 | 72 | efi_rng_protocol_t *rng = NULL; |
|---|
| 71 | | - struct linux_efi_random_seed *seed = NULL; |
|---|
| 72 | 73 | efi_status_t status; |
|---|
| 73 | 74 | |
|---|
| 74 | 75 | status = efi_bs_call(locate_protocol, &rng_proto, NULL, (void **)&rng); |
|---|
| .. | .. |
|---|
| 76 | 77 | return status; |
|---|
| 77 | 78 | |
|---|
| 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 | + /* |
|---|
| 79 | 93 | * Use EFI_ACPI_RECLAIM_MEMORY here so that it is guaranteed that the |
|---|
| 80 | 94 | * allocation will survive a kexec reboot (although we refresh the seed |
|---|
| 81 | 95 | * beforehand) |
|---|
| 82 | 96 | */ |
|---|
| 83 | 97 | status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, |
|---|
| 84 | | - sizeof(*seed) + EFI_RANDOM_SEED_SIZE, |
|---|
| 98 | + struct_size(seed, bits, seed_size), |
|---|
| 85 | 99 | (void **)&seed); |
|---|
| 86 | | - if (status != EFI_SUCCESS) |
|---|
| 87 | | - return status; |
|---|
| 100 | + if (status != EFI_SUCCESS) { |
|---|
| 101 | + efi_warn("Failed to allocate memory for RNG seed.\n"); |
|---|
| 102 | + goto err_warn; |
|---|
| 103 | + } |
|---|
| 88 | 104 | |
|---|
| 89 | 105 | status = efi_call_proto(rng, get_rng, &rng_algo_raw, |
|---|
| 90 | | - EFI_RANDOM_SEED_SIZE, seed->bits); |
|---|
| 106 | + EFI_RANDOM_SEED_SIZE, seed->bits); |
|---|
| 91 | 107 | |
|---|
| 92 | 108 | if (status == EFI_UNSUPPORTED) |
|---|
| 93 | 109 | /* |
|---|
| .. | .. |
|---|
| 100 | 116 | if (status != EFI_SUCCESS) |
|---|
| 101 | 117 | goto err_freepool; |
|---|
| 102 | 118 | |
|---|
| 103 | | - seed->size = EFI_RANDOM_SEED_SIZE; |
|---|
| 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 | + |
|---|
| 104 | 124 | status = efi_bs_call(install_configuration_table, &rng_table_guid, seed); |
|---|
| 105 | 125 | if (status != EFI_SUCCESS) |
|---|
| 106 | 126 | goto err_freepool; |
|---|
| 107 | 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 | + } |
|---|
| 108 | 133 | return EFI_SUCCESS; |
|---|
| 109 | 134 | |
|---|
| 110 | 135 | err_freepool: |
|---|
| 136 | + memzero_explicit(seed, struct_size(seed, bits, seed_size)); |
|---|
| 111 | 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"); |
|---|
| 112 | 142 | return status; |
|---|
| 113 | 143 | } |
|---|