Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/libstub/relocate.c
26483 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
#include <linux/efi.h>
4
#include <asm/efi.h>
5
6
#include "efistub.h"
7
8
/**
9
* efi_low_alloc_above() - allocate pages at or above given address
10
* @size: size of the memory area to allocate
11
* @align: minimum alignment of the allocated memory area. It should
12
* a power of two.
13
* @addr: on exit the address of the allocated memory
14
* @min: minimum address to used for the memory allocation
15
*
16
* Allocate at the lowest possible address that is not below @min as
17
* EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
18
* least EFI_ALLOC_ALIGN. The first allocated page will not below the address
19
* given by @min.
20
*
21
* Return: status code
22
*/
23
efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
24
unsigned long *addr, unsigned long min)
25
{
26
struct efi_boot_memmap *map __free(efi_pool) = NULL;
27
efi_status_t status;
28
unsigned long nr_pages;
29
int i;
30
31
status = efi_get_memory_map(&map, false);
32
if (status != EFI_SUCCESS)
33
return status;
34
35
/*
36
* Enforce minimum alignment that EFI or Linux requires when
37
* requesting a specific address. We are doing page-based (or
38
* larger) allocations, and both the address and size must meet
39
* alignment constraints.
40
*/
41
if (align < EFI_ALLOC_ALIGN)
42
align = EFI_ALLOC_ALIGN;
43
44
size = round_up(size, EFI_ALLOC_ALIGN);
45
nr_pages = size / EFI_PAGE_SIZE;
46
for (i = 0; i < map->map_size / map->desc_size; i++) {
47
efi_memory_desc_t *desc;
48
unsigned long m = (unsigned long)map->map;
49
u64 start, end;
50
51
desc = efi_memdesc_ptr(m, map->desc_size, i);
52
53
if (desc->type != EFI_CONVENTIONAL_MEMORY)
54
continue;
55
56
if (desc->attribute & EFI_MEMORY_HOT_PLUGGABLE)
57
continue;
58
59
if (efi_soft_reserve_enabled() &&
60
(desc->attribute & EFI_MEMORY_SP))
61
continue;
62
63
if (desc->num_pages < nr_pages)
64
continue;
65
66
start = desc->phys_addr;
67
end = start + desc->num_pages * EFI_PAGE_SIZE;
68
69
if (start < min)
70
start = min;
71
72
start = round_up(start, align);
73
if ((start + size) > end)
74
continue;
75
76
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
77
EFI_LOADER_DATA, nr_pages, &start);
78
if (status == EFI_SUCCESS) {
79
*addr = start;
80
break;
81
}
82
}
83
84
if (i == map->map_size / map->desc_size)
85
return EFI_NOT_FOUND;
86
87
return EFI_SUCCESS;
88
}
89
90
/**
91
* efi_relocate_kernel() - copy memory area
92
* @image_addr: pointer to address of memory area to copy
93
* @image_size: size of memory area to copy
94
* @alloc_size: minimum size of memory to allocate, must be greater or
95
* equal to image_size
96
* @preferred_addr: preferred target address
97
* @alignment: minimum alignment of the allocated memory area. It
98
* should be a power of two.
99
* @min_addr: minimum target address
100
*
101
* Copy a memory area to a newly allocated memory area aligned according
102
* to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
103
* is not available, the allocated address will not be below @min_addr.
104
* On exit, @image_addr is updated to the target copy address that was used.
105
*
106
* This function is used to copy the Linux kernel verbatim. It does not apply
107
* any relocation changes.
108
*
109
* Return: status code
110
*/
111
efi_status_t efi_relocate_kernel(unsigned long *image_addr,
112
unsigned long image_size,
113
unsigned long alloc_size,
114
unsigned long preferred_addr,
115
unsigned long alignment,
116
unsigned long min_addr)
117
{
118
unsigned long cur_image_addr;
119
unsigned long new_addr = 0;
120
efi_status_t status;
121
unsigned long nr_pages;
122
efi_physical_addr_t efi_addr = preferred_addr;
123
124
if (!image_addr || !image_size || !alloc_size)
125
return EFI_INVALID_PARAMETER;
126
if (alloc_size < image_size)
127
return EFI_INVALID_PARAMETER;
128
129
cur_image_addr = *image_addr;
130
131
/*
132
* The EFI firmware loader could have placed the kernel image
133
* anywhere in memory, but the kernel has restrictions on the
134
* max physical address it can run at. Some architectures
135
* also have a preferred address, so first try to relocate
136
* to the preferred address. If that fails, allocate as low
137
* as possible while respecting the required alignment.
138
*/
139
nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
140
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
141
EFI_LOADER_DATA, nr_pages, &efi_addr);
142
new_addr = efi_addr;
143
/*
144
* If preferred address allocation failed allocate as low as
145
* possible.
146
*/
147
if (status != EFI_SUCCESS) {
148
status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
149
min_addr);
150
}
151
if (status != EFI_SUCCESS) {
152
efi_err("Failed to allocate usable memory for kernel.\n");
153
return status;
154
}
155
156
/*
157
* We know source/dest won't overlap since both memory ranges
158
* have been allocated by UEFI, so we can safely use memcpy.
159
*/
160
memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
161
162
/* Return the new address of the relocated image. */
163
*image_addr = new_addr;
164
165
return status;
166
}
167
168