Offering: HULK hulk inclusion category: feature bugzilla: 189284
--------------------------------
allow users to mark at most 4 regions as not available for kaslr
Signed-off-by: felix fuzhen5@huawei.com Signed-off-by: Felix Fu fuzhen5@huawei.com --- drivers/firmware/efi/libstub/arm64-stub.c | 118 ++++++++++++++++++ .../firmware/efi/libstub/efi-stub-helper.c | 7 +- drivers/firmware/efi/libstub/efistub.h | 10 ++ drivers/firmware/efi/libstub/randomalloc.c | 17 +++ 4 files changed, 151 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index ae6790218339..ff2fcddbbbbb 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -104,6 +104,124 @@ void free_avoid_memmap(void) } }
+#ifdef CONFIG_NOKASLR_MEM_RANGE +#define MAX_MEM_NOKASLR_REGIONS 4 + +struct mem_region { + unsigned long long start; + unsigned long long size; +}; + +struct mem_region mem_nokaslr[MAX_MEM_NOKASLR_REGIONS]; + +void efi_parse_option_nokaslr_ranges(char *str) +{ + int i = 0; + + while (str && (i < MAX_MEM_NOKASLR_REGIONS)) { + char *oldstr; + u64 start, end; + char *k = strchr(str, ','); + + if (k) + *k++ = 0; + + oldstr = str; + start = memparse(str, &str); + if (str == oldstr || *str != '-') { + efi_warn("Nokaslr values "%s" error.\n", oldstr); + break; + } + end = memparse(str + 1, &str); + if (start >= end) { + efi_warn("Nokaslr values "%s" error, start >= end.\n", oldstr); + break; + } + + mem_nokaslr[i].start = start; + mem_nokaslr[i].size = end - start; + str = k; + i++; + } +} + +static bool mem_overlaps(struct mem_region *one, struct mem_region *two) +{ + if (one->start + one->size <= two->start) + return false; + if (one->start >= two->start + two->size) + return false; + return true; +} + +static bool mem_avoid_overlap(struct mem_region *region, struct mem_region *overlap) +{ + int i; + u64 earliest = region->start + region->size; + bool is_overlapping = false; + + for (i = 0; i < MAX_MEM_NOKASLR_REGIONS; i++) { + if (mem_overlaps(region, &mem_nokaslr[i]) && + mem_nokaslr[i].start < earliest) { + *overlap = mem_nokaslr[i]; + earliest = overlap->start; + is_overlapping = true; + } + } + return is_overlapping; +} + +unsigned long cal_slots_avoid_overlap(efi_memory_desc_t *md, unsigned long size, u8 cal_type, + unsigned long align_shift, unsigned long target) +{ + struct mem_region region, overlap; + unsigned long region_end, first, last; + unsigned long align = 1UL << align_shift; + unsigned long total_slots = 0, slots; + + region.start = md->phys_addr; + region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1, (u64)ULONG_MAX); + + while (region.start < region_end) { + first = round_up(region.start, align); + last = round_down(region_end - size + 1, align); + + if (first > last) + break; + + region.size = region_end - region.start + 1; + + if (!mem_avoid_overlap(®ion, &overlap)) { + slots = ((last - first) >> align_shift) + 1; + total_slots += slots; + + if (cal_type == CAL_SLOTS_PHYADDR) + return first + target * align; + + break; + } + + if (overlap.start >= region.start + size) { + slots = ((round_up(overlap.start - size + 1, align) - first) >> + align_shift) + 1; + total_slots += slots; + + if (cal_type == CAL_SLOTS_PHYADDR) { + if (target > slots) + target -= slots; + else + return first + target * align; + } + } + + /* Clip off the overlapping region and start over. */ + region.start = overlap.start + overlap.size; + } + + return total_slots; +} +#endif + efi_status_t check_platform_features(void) { u64 tg; diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 8670654bf561..dc3fbd0914f5 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -212,7 +212,12 @@ efi_status_t efi_parse_options(char const *cmdline) break;
if (!strcmp(param, "nokaslr")) { - efi_nokaslr = true; +#if defined(CONFIG_NOKASLR_MEM_RANGE) && defined(CONFIG_ARM64) + if (val) + efi_parse_option_nokaslr_ranges(val); + else +#endif + efi_nokaslr = true; } else if (!strcmp(param, "quiet")) { efi_loglevel = CONSOLE_LOGLEVEL_QUIET; } else if (!strcmp(param, "noinitrd")) { diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index b823f76bb739..ee4c57a285e7 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -838,6 +838,16 @@ static inline void mem_avoid_memmap(void) { } static inline void free_avoid_memmap(void) { } #endif
+#if defined(CONFIG_NOKASLR_MEM_RANGE) && defined(CONFIG_ARM64) +#define CAL_SLOTS_NUMBER 0 +#define CAL_SLOTS_PHYADDR 1 + +void efi_parse_option_nokaslr_ranges(char *str); +unsigned long cal_slots_avoid_overlap(efi_memory_desc_t *md, unsigned long size, u8 cal_type, + unsigned long align_shift, unsigned long target); +#endif + + efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, unsigned long size);
diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index 724155b9e10d..3be5120c651d 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -39,7 +39,11 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, if (first_slot > last_slot) return 0;
+#if defined(CONFIG_NOKASLR_MEM_RANGE) && defined(CONFIG_ARM64) + return cal_slots_avoid_overlap(md, size, CAL_SLOTS_NUMBER, align_shift, 0); +#else return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1; +#endif }
/* @@ -88,6 +92,14 @@ efi_status_t efi_random_alloc(unsigned long size, total_slots += slots; }
+#if defined(CONFIG_NOKASLR_MEM_RANGE) && defined(CONFIG_ARM64) + if (total_slots == 0) { + efi_info("Physical KASLR disabled: no suitable momory region!\n"); + efi_bs_call(free_pool, memory_map); + return EFI_OUT_OF_RESOURCES; + } +#endif + /* find a random number between 0 and total_slots */ target_slot = (total_slots * (u64)(random_seed & U32_MAX)) >> 32;
@@ -112,7 +124,12 @@ efi_status_t efi_random_alloc(unsigned long size, continue; }
+#if defined(CONFIG_NOKASLR_MEM_RANGE) && defined(CONFIG_ARM64) + target = cal_slots_avoid_overlap(md, size, CAL_SLOTS_PHYADDR, ilog2(align), + target_slot); +#else target = round_up(md->phys_addr, align) + target_slot * align; +#endif pages = size / EFI_PAGE_SIZE;
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,