cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

randomalloc.c (3967B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2016 Linaro Ltd;  <ard.biesheuvel@linaro.org>
      4 */
      5
      6#include <linux/efi.h>
      7#include <linux/log2.h>
      8#include <asm/efi.h>
      9
     10#include "efistub.h"
     11
     12/*
     13 * Return the number of slots covered by this entry, i.e., the number of
     14 * addresses it covers that are suitably aligned and supply enough room
     15 * for the allocation.
     16 */
     17static unsigned long get_entry_num_slots(efi_memory_desc_t *md,
     18					 unsigned long size,
     19					 unsigned long align_shift)
     20{
     21	unsigned long align = 1UL << align_shift;
     22	u64 first_slot, last_slot, region_end;
     23
     24	if (md->type != EFI_CONVENTIONAL_MEMORY)
     25		return 0;
     26
     27	if (efi_soft_reserve_enabled() &&
     28	    (md->attribute & EFI_MEMORY_SP))
     29		return 0;
     30
     31	region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1,
     32			 (u64)ULONG_MAX);
     33	if (region_end < size)
     34		return 0;
     35
     36	first_slot = round_up(md->phys_addr, align);
     37	last_slot = round_down(region_end - size + 1, align);
     38
     39	if (first_slot > last_slot)
     40		return 0;
     41
     42	return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1;
     43}
     44
     45/*
     46 * The UEFI memory descriptors have a virtual address field that is only used
     47 * when installing the virtual mapping using SetVirtualAddressMap(). Since it
     48 * is unused here, we can reuse it to keep track of each descriptor's slot
     49 * count.
     50 */
     51#define MD_NUM_SLOTS(md)	((md)->virt_addr)
     52
     53efi_status_t efi_random_alloc(unsigned long size,
     54			      unsigned long align,
     55			      unsigned long *addr,
     56			      unsigned long random_seed)
     57{
     58	unsigned long map_size, desc_size, total_slots = 0, target_slot;
     59	unsigned long total_mirrored_slots = 0;
     60	unsigned long buff_size;
     61	efi_status_t status;
     62	efi_memory_desc_t *memory_map;
     63	int map_offset;
     64	struct efi_boot_memmap map;
     65
     66	map.map =	&memory_map;
     67	map.map_size =	&map_size;
     68	map.desc_size =	&desc_size;
     69	map.desc_ver =	NULL;
     70	map.key_ptr =	NULL;
     71	map.buff_size =	&buff_size;
     72
     73	status = efi_get_memory_map(&map);
     74	if (status != EFI_SUCCESS)
     75		return status;
     76
     77	if (align < EFI_ALLOC_ALIGN)
     78		align = EFI_ALLOC_ALIGN;
     79
     80	size = round_up(size, EFI_ALLOC_ALIGN);
     81
     82	/* count the suitable slots in each memory map entry */
     83	for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
     84		efi_memory_desc_t *md = (void *)memory_map + map_offset;
     85		unsigned long slots;
     86
     87		slots = get_entry_num_slots(md, size, ilog2(align));
     88		MD_NUM_SLOTS(md) = slots;
     89		total_slots += slots;
     90		if (md->attribute & EFI_MEMORY_MORE_RELIABLE)
     91			total_mirrored_slots += slots;
     92	}
     93
     94	/* consider only mirrored slots for randomization if any exist */
     95	if (total_mirrored_slots > 0)
     96		total_slots = total_mirrored_slots;
     97
     98	/* find a random number between 0 and total_slots */
     99	target_slot = (total_slots * (u64)(random_seed & U32_MAX)) >> 32;
    100
    101	/*
    102	 * target_slot is now a value in the range [0, total_slots), and so
    103	 * it corresponds with exactly one of the suitable slots we recorded
    104	 * when iterating over the memory map the first time around.
    105	 *
    106	 * So iterate over the memory map again, subtracting the number of
    107	 * slots of each entry at each iteration, until we have found the entry
    108	 * that covers our chosen slot. Use the residual value of target_slot
    109	 * to calculate the randomly chosen address, and allocate it directly
    110	 * using EFI_ALLOCATE_ADDRESS.
    111	 */
    112	for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
    113		efi_memory_desc_t *md = (void *)memory_map + map_offset;
    114		efi_physical_addr_t target;
    115		unsigned long pages;
    116
    117		if (total_mirrored_slots > 0 &&
    118		    !(md->attribute & EFI_MEMORY_MORE_RELIABLE))
    119			continue;
    120
    121		if (target_slot >= MD_NUM_SLOTS(md)) {
    122			target_slot -= MD_NUM_SLOTS(md);
    123			continue;
    124		}
    125
    126		target = round_up(md->phys_addr, align) + target_slot * align;
    127		pages = size / EFI_PAGE_SIZE;
    128
    129		status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
    130				     EFI_LOADER_DATA, pages, &target);
    131		if (status == EFI_SUCCESS)
    132			*addr = target;
    133		break;
    134	}
    135
    136	efi_bs_call(free_pool, memory_map);
    137
    138	return status;
    139}