Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/libstub/randomalloc.c
26483 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 2016 Linaro Ltd; <[email protected]>
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
*/
17
static unsigned long get_entry_num_slots(efi_memory_desc_t *md,
18
unsigned long size,
19
unsigned long align_shift,
20
u64 alloc_min, u64 alloc_max)
21
{
22
unsigned long align = 1UL << align_shift;
23
u64 first_slot, last_slot, region_end;
24
25
if (md->type != EFI_CONVENTIONAL_MEMORY)
26
return 0;
27
28
if (md->attribute & EFI_MEMORY_HOT_PLUGGABLE)
29
return 0;
30
31
if (efi_soft_reserve_enabled() &&
32
(md->attribute & EFI_MEMORY_SP))
33
return 0;
34
35
region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1,
36
alloc_max);
37
if (region_end < size)
38
return 0;
39
40
first_slot = round_up(max(md->phys_addr, alloc_min), align);
41
last_slot = round_down(region_end - size + 1, align);
42
43
if (first_slot > last_slot)
44
return 0;
45
46
return ((unsigned long)(last_slot - first_slot) >> align_shift) + 1;
47
}
48
49
/*
50
* The UEFI memory descriptors have a virtual address field that is only used
51
* when installing the virtual mapping using SetVirtualAddressMap(). Since it
52
* is unused here, we can reuse it to keep track of each descriptor's slot
53
* count.
54
*/
55
#define MD_NUM_SLOTS(md) ((md)->virt_addr)
56
57
efi_status_t efi_random_alloc(unsigned long size,
58
unsigned long align,
59
unsigned long *addr,
60
unsigned long random_seed,
61
int memory_type,
62
unsigned long alloc_min,
63
unsigned long alloc_max)
64
{
65
struct efi_boot_memmap *map __free(efi_pool) = NULL;
66
unsigned long total_slots = 0, target_slot;
67
unsigned long total_mirrored_slots = 0;
68
efi_status_t status;
69
int map_offset;
70
71
status = efi_get_memory_map(&map, false);
72
if (status != EFI_SUCCESS)
73
return status;
74
75
if (align < EFI_ALLOC_ALIGN)
76
align = EFI_ALLOC_ALIGN;
77
78
/* Avoid address 0x0, as it can be mistaken for NULL */
79
if (alloc_min == 0)
80
alloc_min = align;
81
82
size = round_up(size, EFI_ALLOC_ALIGN);
83
84
/* count the suitable slots in each memory map entry */
85
for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) {
86
efi_memory_desc_t *md = (void *)map->map + map_offset;
87
unsigned long slots;
88
89
slots = get_entry_num_slots(md, size, ilog2(align), alloc_min,
90
alloc_max);
91
MD_NUM_SLOTS(md) = slots;
92
total_slots += slots;
93
if (md->attribute & EFI_MEMORY_MORE_RELIABLE)
94
total_mirrored_slots += slots;
95
}
96
97
/* consider only mirrored slots for randomization if any exist */
98
if (total_mirrored_slots > 0)
99
total_slots = total_mirrored_slots;
100
101
/* find a random number between 0 and total_slots */
102
target_slot = (total_slots * (u64)(random_seed & U32_MAX)) >> 32;
103
104
/*
105
* target_slot is now a value in the range [0, total_slots), and so
106
* it corresponds with exactly one of the suitable slots we recorded
107
* when iterating over the memory map the first time around.
108
*
109
* So iterate over the memory map again, subtracting the number of
110
* slots of each entry at each iteration, until we have found the entry
111
* that covers our chosen slot. Use the residual value of target_slot
112
* to calculate the randomly chosen address, and allocate it directly
113
* using EFI_ALLOCATE_ADDRESS.
114
*/
115
status = EFI_OUT_OF_RESOURCES;
116
for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) {
117
efi_memory_desc_t *md = (void *)map->map + map_offset;
118
efi_physical_addr_t target;
119
unsigned long pages;
120
121
if (total_mirrored_slots > 0 &&
122
!(md->attribute & EFI_MEMORY_MORE_RELIABLE))
123
continue;
124
125
if (target_slot >= MD_NUM_SLOTS(md)) {
126
target_slot -= MD_NUM_SLOTS(md);
127
continue;
128
}
129
130
target = round_up(max_t(u64, md->phys_addr, alloc_min), align) + target_slot * align;
131
pages = size / EFI_PAGE_SIZE;
132
133
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
134
memory_type, pages, &target);
135
if (status == EFI_SUCCESS)
136
*addr = target;
137
break;
138
}
139
140
return status;
141
}
142
143