Merge tag 'for-6.6-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[platform/kernel/linux-starfive.git] / drivers / firmware / efi / libstub / unaccepted_memory.c
1 // SPDX-License-Identifier: GPL-2.0-only
2
3 #include <linux/efi.h>
4 #include <asm/efi.h>
5 #include "efistub.h"
6
7 struct efi_unaccepted_memory *unaccepted_table;
8
9 efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc,
10                                         struct efi_boot_memmap *map)
11 {
12         efi_guid_t unaccepted_table_guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID;
13         u64 unaccepted_start = ULLONG_MAX, unaccepted_end = 0, bitmap_size;
14         efi_status_t status;
15         int i;
16
17         /* Check if the table is already installed */
18         unaccepted_table = get_efi_config_table(unaccepted_table_guid);
19         if (unaccepted_table) {
20                 if (unaccepted_table->version != 1) {
21                         efi_err("Unknown version of unaccepted memory table\n");
22                         return EFI_UNSUPPORTED;
23                 }
24                 return EFI_SUCCESS;
25         }
26
27         /* Check if there's any unaccepted memory and find the max address */
28         for (i = 0; i < nr_desc; i++) {
29                 efi_memory_desc_t *d;
30                 unsigned long m = (unsigned long)map->map;
31
32                 d = efi_early_memdesc_ptr(m, map->desc_size, i);
33                 if (d->type != EFI_UNACCEPTED_MEMORY)
34                         continue;
35
36                 unaccepted_start = min(unaccepted_start, d->phys_addr);
37                 unaccepted_end = max(unaccepted_end,
38                                      d->phys_addr + d->num_pages * PAGE_SIZE);
39         }
40
41         if (unaccepted_start == ULLONG_MAX)
42                 return EFI_SUCCESS;
43
44         unaccepted_start = round_down(unaccepted_start,
45                                       EFI_UNACCEPTED_UNIT_SIZE);
46         unaccepted_end = round_up(unaccepted_end, EFI_UNACCEPTED_UNIT_SIZE);
47
48         /*
49          * If unaccepted memory is present, allocate a bitmap to track what
50          * memory has to be accepted before access.
51          *
52          * One bit in the bitmap represents 2MiB in the address space:
53          * A 4k bitmap can track 64GiB of physical address space.
54          *
55          * In the worst case scenario -- a huge hole in the middle of the
56          * address space -- It needs 256MiB to handle 4PiB of the address
57          * space.
58          *
59          * The bitmap will be populated in setup_e820() according to the memory
60          * map after efi_exit_boot_services().
61          */
62         bitmap_size = DIV_ROUND_UP(unaccepted_end - unaccepted_start,
63                                    EFI_UNACCEPTED_UNIT_SIZE * BITS_PER_BYTE);
64
65         status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY,
66                              sizeof(*unaccepted_table) + bitmap_size,
67                              (void **)&unaccepted_table);
68         if (status != EFI_SUCCESS) {
69                 efi_err("Failed to allocate unaccepted memory config table\n");
70                 return status;
71         }
72
73         unaccepted_table->version = 1;
74         unaccepted_table->unit_size = EFI_UNACCEPTED_UNIT_SIZE;
75         unaccepted_table->phys_base = unaccepted_start;
76         unaccepted_table->size = bitmap_size;
77         memset(unaccepted_table->bitmap, 0, bitmap_size);
78
79         status = efi_bs_call(install_configuration_table,
80                              &unaccepted_table_guid, unaccepted_table);
81         if (status != EFI_SUCCESS) {
82                 efi_bs_call(free_pool, unaccepted_table);
83                 efi_err("Failed to install unaccepted memory config table!\n");
84         }
85
86         return status;
87 }
88
89 /*
90  * The accepted memory bitmap only works at unit_size granularity.  Take
91  * unaligned start/end addresses and either:
92  *  1. Accepts the memory immediately and in its entirety
93  *  2. Accepts unaligned parts, and marks *some* aligned part unaccepted
94  *
95  * The function will never reach the bitmap_set() with zero bits to set.
96  */
97 void process_unaccepted_memory(u64 start, u64 end)
98 {
99         u64 unit_size = unaccepted_table->unit_size;
100         u64 unit_mask = unaccepted_table->unit_size - 1;
101         u64 bitmap_size = unaccepted_table->size;
102
103         /*
104          * Ensure that at least one bit will be set in the bitmap by
105          * immediately accepting all regions under 2*unit_size.  This is
106          * imprecise and may immediately accept some areas that could
107          * have been represented in the bitmap.  But, results in simpler
108          * code below
109          *
110          * Consider case like this (assuming unit_size == 2MB):
111          *
112          * | 4k | 2044k |    2048k   |
113          * ^ 0x0        ^ 2MB        ^ 4MB
114          *
115          * Only the first 4k has been accepted. The 0MB->2MB region can not be
116          * represented in the bitmap. The 2MB->4MB region can be represented in
117          * the bitmap. But, the 0MB->4MB region is <2*unit_size and will be
118          * immediately accepted in its entirety.
119          */
120         if (end - start < 2 * unit_size) {
121                 arch_accept_memory(start, end);
122                 return;
123         }
124
125         /*
126          * No matter how the start and end are aligned, at least one unaccepted
127          * unit_size area will remain to be marked in the bitmap.
128          */
129
130         /* Immediately accept a <unit_size piece at the start: */
131         if (start & unit_mask) {
132                 arch_accept_memory(start, round_up(start, unit_size));
133                 start = round_up(start, unit_size);
134         }
135
136         /* Immediately accept a <unit_size piece at the end: */
137         if (end & unit_mask) {
138                 arch_accept_memory(round_down(end, unit_size), end);
139                 end = round_down(end, unit_size);
140         }
141
142         /*
143          * Accept part of the range that before phys_base and cannot be recorded
144          * into the bitmap.
145          */
146         if (start < unaccepted_table->phys_base) {
147                 arch_accept_memory(start,
148                                    min(unaccepted_table->phys_base, end));
149                 start = unaccepted_table->phys_base;
150         }
151
152         /* Nothing to record */
153         if (end < unaccepted_table->phys_base)
154                 return;
155
156         /* Translate to offsets from the beginning of the bitmap */
157         start -= unaccepted_table->phys_base;
158         end -= unaccepted_table->phys_base;
159
160         /* Accept memory that doesn't fit into bitmap */
161         if (end > bitmap_size * unit_size * BITS_PER_BYTE) {
162                 unsigned long phys_start, phys_end;
163
164                 phys_start = bitmap_size * unit_size * BITS_PER_BYTE +
165                              unaccepted_table->phys_base;
166                 phys_end = end + unaccepted_table->phys_base;
167
168                 arch_accept_memory(phys_start, phys_end);
169                 end = bitmap_size * unit_size * BITS_PER_BYTE;
170         }
171
172         /*
173          * 'start' and 'end' are now both unit_size-aligned.
174          * Record the range as being unaccepted:
175          */
176         bitmap_set(unaccepted_table->bitmap,
177                    start / unit_size, (end - start) / unit_size);
178 }
179
180 void accept_memory(phys_addr_t start, phys_addr_t end)
181 {
182         unsigned long range_start, range_end;
183         unsigned long bitmap_size;
184         u64 unit_size;
185
186         if (!unaccepted_table)
187                 return;
188
189         unit_size = unaccepted_table->unit_size;
190
191         /*
192          * Only care for the part of the range that is represented
193          * in the bitmap.
194          */
195         if (start < unaccepted_table->phys_base)
196                 start = unaccepted_table->phys_base;
197         if (end < unaccepted_table->phys_base)
198                 return;
199
200         /* Translate to offsets from the beginning of the bitmap */
201         start -= unaccepted_table->phys_base;
202         end -= unaccepted_table->phys_base;
203
204         /* Make sure not to overrun the bitmap */
205         if (end > unaccepted_table->size * unit_size * BITS_PER_BYTE)
206                 end = unaccepted_table->size * unit_size * BITS_PER_BYTE;
207
208         range_start = start / unit_size;
209         bitmap_size = DIV_ROUND_UP(end, unit_size);
210
211         for_each_set_bitrange_from(range_start, range_end,
212                                    unaccepted_table->bitmap, bitmap_size) {
213                 unsigned long phys_start, phys_end;
214
215                 phys_start = range_start * unit_size + unaccepted_table->phys_base;
216                 phys_end = range_end * unit_size + unaccepted_table->phys_base;
217
218                 arch_accept_memory(phys_start, phys_end);
219                 bitmap_clear(unaccepted_table->bitmap,
220                              range_start, range_end - range_start);
221         }
222 }