1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Helper functions used by the EFI stub on multiple |
4 | * architectures to deal with physical address space randomization. |
5 | */ |
6 | #include <linux/efi.h> |
7 | |
8 | #include "efistub.h" |
9 | |
10 | /** |
11 | * efi_kaslr_get_phys_seed() - Get random seed for physical kernel KASLR |
12 | * @image_handle: Handle to the image |
13 | * |
14 | * If KASLR is not disabled, obtain a random seed using EFI_RNG_PROTOCOL |
15 | * that will be used to move the kernel physical mapping. |
16 | * |
17 | * Return: the random seed |
18 | */ |
19 | u32 efi_kaslr_get_phys_seed(efi_handle_t image_handle) |
20 | { |
21 | efi_status_t status; |
22 | u32 phys_seed; |
23 | efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID; |
24 | void *p; |
25 | |
26 | if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE)) |
27 | return 0; |
28 | |
29 | if (efi_nokaslr) { |
30 | efi_info("KASLR disabled on kernel command line\n" ); |
31 | } else if (efi_bs_call(handle_protocol, image_handle, |
32 | &li_fixed_proto, &p) == EFI_SUCCESS) { |
33 | efi_info("Image placement fixed by loader\n" ); |
34 | } else { |
35 | status = efi_get_random_bytes(size: sizeof(phys_seed), |
36 | out: (u8 *)&phys_seed); |
37 | if (status == EFI_SUCCESS) { |
38 | return phys_seed; |
39 | } else if (status == EFI_NOT_FOUND) { |
40 | efi_info("EFI_RNG_PROTOCOL unavailable\n" ); |
41 | efi_nokaslr = true; |
42 | } else if (status != EFI_SUCCESS) { |
43 | efi_err("efi_get_random_bytes() failed (0x%lx)\n" , |
44 | status); |
45 | efi_nokaslr = true; |
46 | } |
47 | } |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | /* |
53 | * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail |
54 | * to provide space, and fail to zero it). Check for this condition by double |
55 | * checking that the first and the last byte of the image are covered by the |
56 | * same EFI memory map entry. |
57 | */ |
58 | static bool check_image_region(u64 base, u64 size) |
59 | { |
60 | struct efi_boot_memmap *map; |
61 | efi_status_t status; |
62 | bool ret = false; |
63 | int map_offset; |
64 | |
65 | status = efi_get_memory_map(map: &map, install_cfg_tbl: false); |
66 | if (status != EFI_SUCCESS) |
67 | return false; |
68 | |
69 | for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) { |
70 | efi_memory_desc_t *md = (void *)map->map + map_offset; |
71 | u64 end = md->phys_addr + md->num_pages * EFI_PAGE_SIZE; |
72 | |
73 | /* |
74 | * Find the region that covers base, and return whether |
75 | * it covers base+size bytes. |
76 | */ |
77 | if (base >= md->phys_addr && base < end) { |
78 | ret = (base + size) <= end; |
79 | break; |
80 | } |
81 | } |
82 | |
83 | efi_bs_call(free_pool, map); |
84 | |
85 | return ret; |
86 | } |
87 | |
88 | /** |
89 | * efi_kaslr_relocate_kernel() - Relocate the kernel (random if KASLR enabled) |
90 | * @image_addr: Pointer to the current kernel location |
91 | * @reserve_addr: Pointer to the relocated kernel location |
92 | * @reserve_size: Size of the relocated kernel |
93 | * @kernel_size: Size of the text + data |
94 | * @kernel_codesize: Size of the text |
95 | * @kernel_memsize: Size of the text + data + bss |
96 | * @phys_seed: Random seed used for the relocation |
97 | * |
98 | * If KASLR is not enabled, this function relocates the kernel to a fixed |
99 | * address (or leave it as its current location). If KASLR is enabled, the |
100 | * kernel physical location is randomized using the seed in parameter. |
101 | * |
102 | * Return: status code, EFI_SUCCESS if relocation is successful |
103 | */ |
104 | efi_status_t efi_kaslr_relocate_kernel(unsigned long *image_addr, |
105 | unsigned long *reserve_addr, |
106 | unsigned long *reserve_size, |
107 | unsigned long kernel_size, |
108 | unsigned long kernel_codesize, |
109 | unsigned long kernel_memsize, |
110 | u32 phys_seed) |
111 | { |
112 | efi_status_t status; |
113 | u64 min_kimg_align = efi_get_kimg_min_align(); |
114 | |
115 | if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { |
116 | /* |
117 | * If KASLR is enabled, and we have some randomness available, |
118 | * locate the kernel at a randomized offset in physical memory. |
119 | */ |
120 | status = efi_random_alloc(size: *reserve_size, align: min_kimg_align, |
121 | addr: reserve_addr, random_seed: phys_seed, |
122 | EFI_LOADER_CODE, alloc_min: 0, EFI_ALLOC_LIMIT); |
123 | if (status != EFI_SUCCESS) |
124 | efi_warn("efi_random_alloc() failed: 0x%lx\n" , status); |
125 | } else { |
126 | status = EFI_OUT_OF_RESOURCES; |
127 | } |
128 | |
129 | if (status != EFI_SUCCESS) { |
130 | if (!check_image_region(base: *image_addr, size: kernel_memsize)) { |
131 | efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n" ); |
132 | } else if (IS_ALIGNED(*image_addr, min_kimg_align) && |
133 | (unsigned long)_end < EFI_ALLOC_LIMIT) { |
134 | /* |
135 | * Just execute from wherever we were loaded by the |
136 | * UEFI PE/COFF loader if the placement is suitable. |
137 | */ |
138 | *reserve_size = 0; |
139 | return EFI_SUCCESS; |
140 | } |
141 | |
142 | status = efi_allocate_pages_aligned(size: *reserve_size, addr: reserve_addr, |
143 | ULONG_MAX, align: min_kimg_align, |
144 | EFI_LOADER_CODE); |
145 | |
146 | if (status != EFI_SUCCESS) { |
147 | efi_err("Failed to relocate kernel\n" ); |
148 | *reserve_size = 0; |
149 | return status; |
150 | } |
151 | } |
152 | |
153 | memcpy(to: (void *)*reserve_addr, from: (void *)*image_addr, len: kernel_size); |
154 | *image_addr = *reserve_addr; |
155 | efi_icache_sync(*image_addr, *image_addr + kernel_codesize); |
156 | efi_remap_image(image_base: *image_addr, alloc_size: *reserve_size, code_size: kernel_codesize); |
157 | |
158 | return status; |
159 | } |
160 | |