Path: blob/master/drivers/firmware/efi/libstub/randomalloc.c
26483 views
// SPDX-License-Identifier: GPL-2.01/*2* Copyright (C) 2016 Linaro Ltd; <[email protected]>3*/45#include <linux/efi.h>6#include <linux/log2.h>7#include <asm/efi.h>89#include "efistub.h"1011/*12* Return the number of slots covered by this entry, i.e., the number of13* addresses it covers that are suitably aligned and supply enough room14* for the allocation.15*/16static unsigned long get_entry_num_slots(efi_memory_desc_t *md,17unsigned long size,18unsigned long align_shift,19u64 alloc_min, u64 alloc_max)20{21unsigned long align = 1UL << align_shift;22u64 first_slot, last_slot, region_end;2324if (md->type != EFI_CONVENTIONAL_MEMORY)25return 0;2627if (md->attribute & EFI_MEMORY_HOT_PLUGGABLE)28return 0;2930if (efi_soft_reserve_enabled() &&31(md->attribute & EFI_MEMORY_SP))32return 0;3334region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1,35alloc_max);36if (region_end < size)37return 0;3839first_slot = round_up(max(md->phys_addr, alloc_min), align);40last_slot = round_down(region_end - size + 1, align);4142if (first_slot > last_slot)43return 0;4445return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1;46}4748/*49* The UEFI memory descriptors have a virtual address field that is only used50* when installing the virtual mapping using SetVirtualAddressMap(). Since it51* is unused here, we can reuse it to keep track of each descriptor's slot52* count.53*/54#define MD_NUM_SLOTS(md) ((md)->virt_addr)5556efi_status_t efi_random_alloc(unsigned long size,57unsigned long align,58unsigned long *addr,59unsigned long random_seed,60int memory_type,61unsigned long alloc_min,62unsigned long alloc_max)63{64struct efi_boot_memmap *map __free(efi_pool) = NULL;65unsigned long total_slots = 0, target_slot;66unsigned long total_mirrored_slots = 0;67efi_status_t status;68int map_offset;6970status = efi_get_memory_map(&map, false);71if (status != EFI_SUCCESS)72return status;7374if (align < EFI_ALLOC_ALIGN)75align = EFI_ALLOC_ALIGN;7677/* Avoid address 0x0, as it can be mistaken for NULL */78if (alloc_min == 0)79alloc_min = align;8081size = round_up(size, EFI_ALLOC_ALIGN);8283/* count the suitable slots in each memory map entry */84for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) {85efi_memory_desc_t *md = (void *)map->map + map_offset;86unsigned long slots;8788slots = get_entry_num_slots(md, size, ilog2(align), alloc_min,89alloc_max);90MD_NUM_SLOTS(md) = slots;91total_slots += slots;92if (md->attribute & EFI_MEMORY_MORE_RELIABLE)93total_mirrored_slots += slots;94}9596/* consider only mirrored slots for randomization if any exist */97if (total_mirrored_slots > 0)98total_slots = total_mirrored_slots;99100/* find a random number between 0 and total_slots */101target_slot = (total_slots * (u64)(random_seed & U32_MAX)) >> 32;102103/*104* target_slot is now a value in the range [0, total_slots), and so105* it corresponds with exactly one of the suitable slots we recorded106* when iterating over the memory map the first time around.107*108* So iterate over the memory map again, subtracting the number of109* slots of each entry at each iteration, until we have found the entry110* that covers our chosen slot. Use the residual value of target_slot111* to calculate the randomly chosen address, and allocate it directly112* using EFI_ALLOCATE_ADDRESS.113*/114status = EFI_OUT_OF_RESOURCES;115for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) {116efi_memory_desc_t *md = (void *)map->map + map_offset;117efi_physical_addr_t target;118unsigned long pages;119120if (total_mirrored_slots > 0 &&121!(md->attribute & EFI_MEMORY_MORE_RELIABLE))122continue;123124if (target_slot >= MD_NUM_SLOTS(md)) {125target_slot -= MD_NUM_SLOTS(md);126continue;127}128129target = round_up(max_t(u64, md->phys_addr, alloc_min), align) + target_slot * align;130pages = size / EFI_PAGE_SIZE;131132status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,133memory_type, pages, &target);134if (status == EFI_SUCCESS)135*addr = target;136break;137}138139return status;140}141142143