10ed02bdaSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 20ed02bdaSArd Biesheuvel /* 30ed02bdaSArd Biesheuvel * Copyright (C) 2016 Linaro Ltd; <ard.biesheuvel@linaro.org> 40ed02bdaSArd Biesheuvel */ 50ed02bdaSArd Biesheuvel 60ed02bdaSArd Biesheuvel #include <linux/efi.h> 70ed02bdaSArd Biesheuvel #include <linux/log2.h> 80ed02bdaSArd Biesheuvel #include <asm/efi.h> 90ed02bdaSArd Biesheuvel 100ed02bdaSArd Biesheuvel #include "efistub.h" 110ed02bdaSArd Biesheuvel 120ed02bdaSArd Biesheuvel /* 130ed02bdaSArd Biesheuvel * Return the number of slots covered by this entry, i.e., the number of 140ed02bdaSArd Biesheuvel * addresses it covers that are suitably aligned and supply enough room 150ed02bdaSArd Biesheuvel * for the allocation. 160ed02bdaSArd Biesheuvel */ 170ed02bdaSArd Biesheuvel static unsigned long get_entry_num_slots(efi_memory_desc_t *md, 180ed02bdaSArd Biesheuvel unsigned long size, 190ed02bdaSArd Biesheuvel unsigned long align_shift) 200ed02bdaSArd Biesheuvel { 210ed02bdaSArd Biesheuvel unsigned long align = 1UL << align_shift; 220ed02bdaSArd Biesheuvel u64 first_slot, last_slot, region_end; 230ed02bdaSArd Biesheuvel 240ed02bdaSArd Biesheuvel if (md->type != EFI_CONVENTIONAL_MEMORY) 250ed02bdaSArd Biesheuvel return 0; 260ed02bdaSArd Biesheuvel 270ed02bdaSArd Biesheuvel if (efi_soft_reserve_enabled() && 280ed02bdaSArd Biesheuvel (md->attribute & EFI_MEMORY_SP)) 290ed02bdaSArd Biesheuvel return 0; 300ed02bdaSArd Biesheuvel 310ed02bdaSArd Biesheuvel region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1, 320ed02bdaSArd Biesheuvel (u64)ULONG_MAX); 330ed02bdaSArd Biesheuvel 340ed02bdaSArd Biesheuvel first_slot = round_up(md->phys_addr, align); 350ed02bdaSArd Biesheuvel last_slot = round_down(region_end - size + 1, align); 360ed02bdaSArd Biesheuvel 370ed02bdaSArd Biesheuvel if (first_slot > last_slot) 380ed02bdaSArd Biesheuvel return 0; 390ed02bdaSArd Biesheuvel 400ed02bdaSArd Biesheuvel return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1; 410ed02bdaSArd Biesheuvel } 420ed02bdaSArd Biesheuvel 430ed02bdaSArd Biesheuvel /* 440ed02bdaSArd Biesheuvel * The UEFI memory descriptors have a virtual address field that is only used 450ed02bdaSArd Biesheuvel * when installing the virtual mapping using SetVirtualAddressMap(). Since it 460ed02bdaSArd Biesheuvel * is unused here, we can reuse it to keep track of each descriptor's slot 470ed02bdaSArd Biesheuvel * count. 480ed02bdaSArd Biesheuvel */ 490ed02bdaSArd Biesheuvel #define MD_NUM_SLOTS(md) ((md)->virt_addr) 500ed02bdaSArd Biesheuvel 510ed02bdaSArd Biesheuvel efi_status_t efi_random_alloc(unsigned long size, 520ed02bdaSArd Biesheuvel unsigned long align, 530ed02bdaSArd Biesheuvel unsigned long *addr, 540ed02bdaSArd Biesheuvel unsigned long random_seed) 550ed02bdaSArd Biesheuvel { 560ed02bdaSArd Biesheuvel unsigned long map_size, desc_size, total_slots = 0, target_slot; 570ed02bdaSArd Biesheuvel unsigned long buff_size; 580ed02bdaSArd Biesheuvel efi_status_t status; 590ed02bdaSArd Biesheuvel efi_memory_desc_t *memory_map; 600ed02bdaSArd Biesheuvel int map_offset; 610ed02bdaSArd Biesheuvel struct efi_boot_memmap map; 620ed02bdaSArd Biesheuvel 630ed02bdaSArd Biesheuvel map.map = &memory_map; 640ed02bdaSArd Biesheuvel map.map_size = &map_size; 650ed02bdaSArd Biesheuvel map.desc_size = &desc_size; 660ed02bdaSArd Biesheuvel map.desc_ver = NULL; 670ed02bdaSArd Biesheuvel map.key_ptr = NULL; 680ed02bdaSArd Biesheuvel map.buff_size = &buff_size; 690ed02bdaSArd Biesheuvel 700ed02bdaSArd Biesheuvel status = efi_get_memory_map(&map); 710ed02bdaSArd Biesheuvel if (status != EFI_SUCCESS) 720ed02bdaSArd Biesheuvel return status; 730ed02bdaSArd Biesheuvel 740ed02bdaSArd Biesheuvel if (align < EFI_ALLOC_ALIGN) 750ed02bdaSArd Biesheuvel align = EFI_ALLOC_ALIGN; 760ed02bdaSArd Biesheuvel 770ed02bdaSArd Biesheuvel /* count the suitable slots in each memory map entry */ 780ed02bdaSArd Biesheuvel for (map_offset = 0; map_offset < map_size; map_offset += desc_size) { 790ed02bdaSArd Biesheuvel efi_memory_desc_t *md = (void *)memory_map + map_offset; 800ed02bdaSArd Biesheuvel unsigned long slots; 810ed02bdaSArd Biesheuvel 820ed02bdaSArd Biesheuvel slots = get_entry_num_slots(md, size, ilog2(align)); 830ed02bdaSArd Biesheuvel MD_NUM_SLOTS(md) = slots; 840ed02bdaSArd Biesheuvel total_slots += slots; 850ed02bdaSArd Biesheuvel } 860ed02bdaSArd Biesheuvel 870ed02bdaSArd Biesheuvel /* find a random number between 0 and total_slots */ 880ed02bdaSArd Biesheuvel target_slot = (total_slots * (u16)random_seed) >> 16; 890ed02bdaSArd Biesheuvel 900ed02bdaSArd Biesheuvel /* 910ed02bdaSArd Biesheuvel * target_slot is now a value in the range [0, total_slots), and so 920ed02bdaSArd Biesheuvel * it corresponds with exactly one of the suitable slots we recorded 930ed02bdaSArd Biesheuvel * when iterating over the memory map the first time around. 940ed02bdaSArd Biesheuvel * 950ed02bdaSArd Biesheuvel * So iterate over the memory map again, subtracting the number of 960ed02bdaSArd Biesheuvel * slots of each entry at each iteration, until we have found the entry 970ed02bdaSArd Biesheuvel * that covers our chosen slot. Use the residual value of target_slot 980ed02bdaSArd Biesheuvel * to calculate the randomly chosen address, and allocate it directly 990ed02bdaSArd Biesheuvel * using EFI_ALLOCATE_ADDRESS. 1000ed02bdaSArd Biesheuvel */ 1010ed02bdaSArd Biesheuvel for (map_offset = 0; map_offset < map_size; map_offset += desc_size) { 1020ed02bdaSArd Biesheuvel efi_memory_desc_t *md = (void *)memory_map + map_offset; 1030ed02bdaSArd Biesheuvel efi_physical_addr_t target; 1040ed02bdaSArd Biesheuvel unsigned long pages; 1050ed02bdaSArd Biesheuvel 1060ed02bdaSArd Biesheuvel if (target_slot >= MD_NUM_SLOTS(md)) { 1070ed02bdaSArd Biesheuvel target_slot -= MD_NUM_SLOTS(md); 1080ed02bdaSArd Biesheuvel continue; 1090ed02bdaSArd Biesheuvel } 1100ed02bdaSArd Biesheuvel 1110ed02bdaSArd Biesheuvel target = round_up(md->phys_addr, align) + target_slot * align; 1120ed02bdaSArd Biesheuvel pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; 1130ed02bdaSArd Biesheuvel 1140ed02bdaSArd Biesheuvel status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, 1150ed02bdaSArd Biesheuvel EFI_LOADER_DATA, pages, &target); 1160ed02bdaSArd Biesheuvel if (status == EFI_SUCCESS) 1170ed02bdaSArd Biesheuvel *addr = target; 1180ed02bdaSArd Biesheuvel break; 1190ed02bdaSArd Biesheuvel } 1200ed02bdaSArd Biesheuvel 1210ed02bdaSArd Biesheuvel efi_bs_call(free_pool, memory_map); 1220ed02bdaSArd Biesheuvel 1230ed02bdaSArd Biesheuvel return status; 1240ed02bdaSArd Biesheuvel } 125