diff options
author | Ard Biesheuvel <ard.biesheuvel@linaro.org> | 2016-01-26 08:48:29 -0500 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2016-02-24 09:57:29 -0500 |
commit | 2b5fe07a78a09a32002642b8a823428ade611f16 (patch) | |
tree | db8929655f2d9de7827d68fbfad1ea08d1cd0e29 /drivers/firmware/efi | |
parent | 48fcb2d0216103d15306caa4814e2381104df6d8 (diff) |
arm64: efi: invoke EFI_RNG_PROTOCOL to supply KASLR randomness
Since arm64 does not use a decompressor that supplies an execution
environment where it is feasible to some extent to provide a source of
randomness, the arm64 KASLR kernel depends on the bootloader to supply
some random bits in the /chosen/kaslr-seed DT property upon kernel entry.
On UEFI systems, we can use the EFI_RNG_PROTOCOL, if supplied, to obtain
some random bits. At the same time, use it to randomize the offset of the
kernel Image in physical memory.
Reviewed-by: Matt Fleming <matt@codeblueprint.co.uk>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'drivers/firmware/efi')
-rw-r--r-- | drivers/firmware/efi/libstub/arm-stub.c | 40 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/arm64-stub.c | 78 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/fdt.c | 14 |
3 files changed, 97 insertions, 35 deletions
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 3397902e4040..4deb3e7faa0e 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c | |||
@@ -18,6 +18,8 @@ | |||
18 | 18 | ||
19 | #include "efistub.h" | 19 | #include "efistub.h" |
20 | 20 | ||
21 | bool __nokaslr; | ||
22 | |||
21 | static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg) | 23 | static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg) |
22 | { | 24 | { |
23 | static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID; | 25 | static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID; |
@@ -207,14 +209,6 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, | |||
207 | pr_efi_err(sys_table, "Failed to find DRAM base\n"); | 209 | pr_efi_err(sys_table, "Failed to find DRAM base\n"); |
208 | goto fail; | 210 | goto fail; |
209 | } | 211 | } |
210 | status = handle_kernel_image(sys_table, image_addr, &image_size, | ||
211 | &reserve_addr, | ||
212 | &reserve_size, | ||
213 | dram_base, image); | ||
214 | if (status != EFI_SUCCESS) { | ||
215 | pr_efi_err(sys_table, "Failed to relocate kernel\n"); | ||
216 | goto fail; | ||
217 | } | ||
218 | 212 | ||
219 | /* | 213 | /* |
220 | * Get the command line from EFI, using the LOADED_IMAGE | 214 | * Get the command line from EFI, using the LOADED_IMAGE |
@@ -224,7 +218,28 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, | |||
224 | cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size); | 218 | cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size); |
225 | if (!cmdline_ptr) { | 219 | if (!cmdline_ptr) { |
226 | pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n"); | 220 | pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n"); |
227 | goto fail_free_image; | 221 | goto fail; |
222 | } | ||
223 | |||
224 | /* check whether 'nokaslr' was passed on the command line */ | ||
225 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { | ||
226 | static const u8 default_cmdline[] = CONFIG_CMDLINE; | ||
227 | const u8 *str, *cmdline = cmdline_ptr; | ||
228 | |||
229 | if (IS_ENABLED(CONFIG_CMDLINE_FORCE)) | ||
230 | cmdline = default_cmdline; | ||
231 | str = strstr(cmdline, "nokaslr"); | ||
232 | if (str == cmdline || (str > cmdline && *(str - 1) == ' ')) | ||
233 | __nokaslr = true; | ||
234 | } | ||
235 | |||
236 | status = handle_kernel_image(sys_table, image_addr, &image_size, | ||
237 | &reserve_addr, | ||
238 | &reserve_size, | ||
239 | dram_base, image); | ||
240 | if (status != EFI_SUCCESS) { | ||
241 | pr_efi_err(sys_table, "Failed to relocate kernel\n"); | ||
242 | goto fail_free_cmdline; | ||
228 | } | 243 | } |
229 | 244 | ||
230 | status = efi_parse_options(cmdline_ptr); | 245 | status = efi_parse_options(cmdline_ptr); |
@@ -244,7 +259,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, | |||
244 | 259 | ||
245 | if (status != EFI_SUCCESS) { | 260 | if (status != EFI_SUCCESS) { |
246 | pr_efi_err(sys_table, "Failed to load device tree!\n"); | 261 | pr_efi_err(sys_table, "Failed to load device tree!\n"); |
247 | goto fail_free_cmdline; | 262 | goto fail_free_image; |
248 | } | 263 | } |
249 | } | 264 | } |
250 | 265 | ||
@@ -286,12 +301,11 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, | |||
286 | efi_free(sys_table, initrd_size, initrd_addr); | 301 | efi_free(sys_table, initrd_size, initrd_addr); |
287 | efi_free(sys_table, fdt_size, fdt_addr); | 302 | efi_free(sys_table, fdt_size, fdt_addr); |
288 | 303 | ||
289 | fail_free_cmdline: | ||
290 | efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr); | ||
291 | |||
292 | fail_free_image: | 304 | fail_free_image: |
293 | efi_free(sys_table, image_size, *image_addr); | 305 | efi_free(sys_table, image_size, *image_addr); |
294 | efi_free(sys_table, reserve_size, reserve_addr); | 306 | efi_free(sys_table, reserve_size, reserve_addr); |
307 | fail_free_cmdline: | ||
308 | efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr); | ||
295 | fail: | 309 | fail: |
296 | return EFI_ERROR; | 310 | return EFI_ERROR; |
297 | } | 311 | } |
diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 78dfbd34b6bf..e0e6b74fef8f 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c | |||
@@ -13,6 +13,10 @@ | |||
13 | #include <asm/efi.h> | 13 | #include <asm/efi.h> |
14 | #include <asm/sections.h> | 14 | #include <asm/sections.h> |
15 | 15 | ||
16 | #include "efistub.h" | ||
17 | |||
18 | extern bool __nokaslr; | ||
19 | |||
16 | efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, | 20 | efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, |
17 | unsigned long *image_addr, | 21 | unsigned long *image_addr, |
18 | unsigned long *image_size, | 22 | unsigned long *image_size, |
@@ -23,26 +27,52 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, | |||
23 | { | 27 | { |
24 | efi_status_t status; | 28 | efi_status_t status; |
25 | unsigned long kernel_size, kernel_memsize = 0; | 29 | unsigned long kernel_size, kernel_memsize = 0; |
26 | unsigned long nr_pages; | ||
27 | void *old_image_addr = (void *)*image_addr; | 30 | void *old_image_addr = (void *)*image_addr; |
28 | unsigned long preferred_offset; | 31 | unsigned long preferred_offset; |
32 | u64 phys_seed = 0; | ||
33 | |||
34 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { | ||
35 | if (!__nokaslr) { | ||
36 | status = efi_get_random_bytes(sys_table_arg, | ||
37 | sizeof(phys_seed), | ||
38 | (u8 *)&phys_seed); | ||
39 | if (status == EFI_NOT_FOUND) { | ||
40 | pr_efi(sys_table_arg, "EFI_RNG_PROTOCOL unavailable, no randomness supplied\n"); | ||
41 | } else if (status != EFI_SUCCESS) { | ||
42 | pr_efi_err(sys_table_arg, "efi_get_random_bytes() failed\n"); | ||
43 | return status; | ||
44 | } | ||
45 | } else { | ||
46 | pr_efi(sys_table_arg, "KASLR disabled on kernel command line\n"); | ||
47 | } | ||
48 | } | ||
29 | 49 | ||
30 | /* | 50 | /* |
31 | * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond | 51 | * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond |
32 | * a 2 MB aligned base, which itself may be lower than dram_base, as | 52 | * a 2 MB aligned base, which itself may be lower than dram_base, as |
33 | * long as the resulting offset equals or exceeds it. | 53 | * long as the resulting offset equals or exceeds it. |
34 | */ | 54 | */ |
35 | preferred_offset = round_down(dram_base, SZ_2M) + TEXT_OFFSET; | 55 | preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET; |
36 | if (preferred_offset < dram_base) | 56 | if (preferred_offset < dram_base) |
37 | preferred_offset += SZ_2M; | 57 | preferred_offset += MIN_KIMG_ALIGN; |
38 | 58 | ||
39 | /* Relocate the image, if required. */ | ||
40 | kernel_size = _edata - _text; | 59 | kernel_size = _edata - _text; |
41 | if (*image_addr != preferred_offset) { | 60 | kernel_memsize = kernel_size + (_end - _edata); |
42 | kernel_memsize = kernel_size + (_end - _edata); | 61 | |
62 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { | ||
63 | /* | ||
64 | * If KASLR is enabled, and we have some randomness available, | ||
65 | * locate the kernel at a randomized offset in physical memory. | ||
66 | */ | ||
67 | *reserve_size = kernel_memsize + TEXT_OFFSET; | ||
68 | status = efi_random_alloc(sys_table_arg, *reserve_size, | ||
69 | MIN_KIMG_ALIGN, reserve_addr, | ||
70 | phys_seed); | ||
43 | 71 | ||
72 | *image_addr = *reserve_addr + TEXT_OFFSET; | ||
73 | } else { | ||
44 | /* | 74 | /* |
45 | * First, try a straight allocation at the preferred offset. | 75 | * Else, try a straight allocation at the preferred offset. |
46 | * This will work around the issue where, if dram_base == 0x0, | 76 | * This will work around the issue where, if dram_base == 0x0, |
47 | * efi_low_alloc() refuses to allocate at 0x0 (to prevent the | 77 | * efi_low_alloc() refuses to allocate at 0x0 (to prevent the |
48 | * address of the allocation to be mistaken for a FAIL return | 78 | * address of the allocation to be mistaken for a FAIL return |
@@ -52,27 +82,31 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, | |||
52 | * Mustang), we can still place the kernel at the address | 82 | * Mustang), we can still place the kernel at the address |
53 | * 'dram_base + TEXT_OFFSET'. | 83 | * 'dram_base + TEXT_OFFSET'. |
54 | */ | 84 | */ |
85 | if (*image_addr == preferred_offset) | ||
86 | return EFI_SUCCESS; | ||
87 | |||
55 | *image_addr = *reserve_addr = preferred_offset; | 88 | *image_addr = *reserve_addr = preferred_offset; |
56 | nr_pages = round_up(kernel_memsize, EFI_ALLOC_ALIGN) / | 89 | *reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN); |
57 | EFI_PAGE_SIZE; | 90 | |
58 | status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, | 91 | status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, |
59 | EFI_LOADER_DATA, nr_pages, | 92 | EFI_LOADER_DATA, |
93 | *reserve_size / EFI_PAGE_SIZE, | ||
60 | (efi_physical_addr_t *)reserve_addr); | 94 | (efi_physical_addr_t *)reserve_addr); |
61 | if (status != EFI_SUCCESS) { | 95 | } |
62 | kernel_memsize += TEXT_OFFSET; | ||
63 | status = efi_low_alloc(sys_table_arg, kernel_memsize, | ||
64 | SZ_2M, reserve_addr); | ||
65 | 96 | ||
66 | if (status != EFI_SUCCESS) { | 97 | if (status != EFI_SUCCESS) { |
67 | pr_efi_err(sys_table_arg, "Failed to relocate kernel\n"); | 98 | *reserve_size = kernel_memsize + TEXT_OFFSET; |
68 | return status; | 99 | status = efi_low_alloc(sys_table_arg, *reserve_size, |
69 | } | 100 | MIN_KIMG_ALIGN, reserve_addr); |
70 | *image_addr = *reserve_addr + TEXT_OFFSET; | 101 | |
102 | if (status != EFI_SUCCESS) { | ||
103 | pr_efi_err(sys_table_arg, "Failed to relocate kernel\n"); | ||
104 | *reserve_size = 0; | ||
105 | return status; | ||
71 | } | 106 | } |
72 | memcpy((void *)*image_addr, old_image_addr, kernel_size); | 107 | *image_addr = *reserve_addr + TEXT_OFFSET; |
73 | *reserve_size = kernel_memsize; | ||
74 | } | 108 | } |
75 | 109 | memcpy((void *)*image_addr, old_image_addr, kernel_size); | |
76 | 110 | ||
77 | return EFI_SUCCESS; | 111 | return EFI_SUCCESS; |
78 | } | 112 | } |
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index cf7b7d46302a..6dba78aef337 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c | |||
@@ -147,6 +147,20 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, | |||
147 | if (status) | 147 | if (status) |
148 | goto fdt_set_fail; | 148 | goto fdt_set_fail; |
149 | 149 | ||
150 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { | ||
151 | efi_status_t efi_status; | ||
152 | |||
153 | efi_status = efi_get_random_bytes(sys_table, sizeof(fdt_val64), | ||
154 | (u8 *)&fdt_val64); | ||
155 | if (efi_status == EFI_SUCCESS) { | ||
156 | status = fdt_setprop(fdt, node, "kaslr-seed", | ||
157 | &fdt_val64, sizeof(fdt_val64)); | ||
158 | if (status) | ||
159 | goto fdt_set_fail; | ||
160 | } else if (efi_status != EFI_NOT_FOUND) { | ||
161 | return efi_status; | ||
162 | } | ||
163 | } | ||
150 | return EFI_SUCCESS; | 164 | return EFI_SUCCESS; |
151 | 165 | ||
152 | fdt_set_fail: | 166 | fdt_set_fail: |