GNU Linux-libre 6.8.9-gnu
[releases.git] / arch / x86 / platform / efi / fake_mem.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * fake_mem.c
4  *
5  * Copyright (C) 2015 FUJITSU LIMITED
6  * Author: Taku Izumi <izumi.taku@jp.fujitsu.com>
7  *
8  * This code introduces new boot option named "efi_fake_mem"
9  * By specifying this parameter, you can add arbitrary attribute to
10  * specific memory range by updating original (firmware provided) EFI
11  * memmap.
12  */
13
14 #include <linux/kernel.h>
15 #include <linux/efi.h>
16 #include <linux/init.h>
17 #include <linux/memblock.h>
18 #include <linux/types.h>
19 #include <linux/sort.h>
20 #include <asm/e820/api.h>
21 #include <asm/efi.h>
22
23 #define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
24
25 static struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM];
26 static int nr_fake_mem;
27
28 static int __init cmp_fake_mem(const void *x1, const void *x2)
29 {
30         const struct efi_mem_range *m1 = x1;
31         const struct efi_mem_range *m2 = x2;
32
33         if (m1->range.start < m2->range.start)
34                 return -1;
35         if (m1->range.start > m2->range.start)
36                 return 1;
37         return 0;
38 }
39
40 static void __init efi_fake_range(struct efi_mem_range *efi_range)
41 {
42         struct efi_memory_map_data data = { 0 };
43         int new_nr_map = efi.memmap.nr_map;
44         efi_memory_desc_t *md;
45         void *new_memmap;
46
47         /* count up the number of EFI memory descriptor */
48         for_each_efi_memory_desc(md)
49                 new_nr_map += efi_memmap_split_count(md, &efi_range->range);
50
51         /* allocate memory for new EFI memmap */
52         if (efi_memmap_alloc(new_nr_map, &data) != 0)
53                 return;
54
55         /* create new EFI memmap */
56         new_memmap = early_memremap(data.phys_map, data.size);
57         if (!new_memmap) {
58                 __efi_memmap_free(data.phys_map, data.size, data.flags);
59                 return;
60         }
61
62         efi_memmap_insert(&efi.memmap, new_memmap, efi_range);
63
64         /* swap into new EFI memmap */
65         early_memunmap(new_memmap, data.size);
66
67         efi_memmap_install(&data);
68 }
69
70 void __init efi_fake_memmap(void)
71 {
72         int i;
73
74         if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem)
75                 return;
76
77         for (i = 0; i < nr_fake_mem; i++)
78                 efi_fake_range(&efi_fake_mems[i]);
79
80         /* print new EFI memmap */
81         efi_print_memmap();
82 }
83
84 static int __init setup_fake_mem(char *p)
85 {
86         u64 start = 0, mem_size = 0, attribute = 0;
87         int i;
88
89         if (!p)
90                 return -EINVAL;
91
92         while (*p != '\0') {
93                 mem_size = memparse(p, &p);
94                 if (*p == '@')
95                         start = memparse(p+1, &p);
96                 else
97                         break;
98
99                 if (*p == ':')
100                         attribute = simple_strtoull(p+1, &p, 0);
101                 else
102                         break;
103
104                 if (nr_fake_mem >= EFI_MAX_FAKEMEM)
105                         break;
106
107                 efi_fake_mems[nr_fake_mem].range.start = start;
108                 efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1;
109                 efi_fake_mems[nr_fake_mem].attribute = attribute;
110                 nr_fake_mem++;
111
112                 if (*p == ',')
113                         p++;
114         }
115
116         sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
117              cmp_fake_mem, NULL);
118
119         for (i = 0; i < nr_fake_mem; i++)
120                 pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
121                         efi_fake_mems[i].attribute, efi_fake_mems[i].range.start,
122                         efi_fake_mems[i].range.end);
123
124         return *p == '\0' ? 0 : -EINVAL;
125 }
126
127 early_param("efi_fake_mem", setup_fake_mem);
128
129 void __init efi_fake_memmap_early(void)
130 {
131         int i;
132
133         /*
134          * The late efi_fake_mem() call can handle all requests if
135          * EFI_MEMORY_SP support is disabled.
136          */
137         if (!efi_soft_reserve_enabled())
138                 return;
139
140         if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem)
141                 return;
142
143         /*
144          * Given that efi_fake_memmap() needs to perform memblock
145          * allocations it needs to run after e820__memblock_setup().
146          * However, if efi_fake_mem specifies EFI_MEMORY_SP for a given
147          * address range that potentially needs to mark the memory as
148          * reserved prior to e820__memblock_setup(). Update e820
149          * directly if EFI_MEMORY_SP is specified for an
150          * EFI_CONVENTIONAL_MEMORY descriptor.
151          */
152         for (i = 0; i < nr_fake_mem; i++) {
153                 struct efi_mem_range *mem = &efi_fake_mems[i];
154                 efi_memory_desc_t *md;
155                 u64 m_start, m_end;
156
157                 if ((mem->attribute & EFI_MEMORY_SP) == 0)
158                         continue;
159
160                 m_start = mem->range.start;
161                 m_end = mem->range.end;
162                 for_each_efi_memory_desc(md) {
163                         u64 start, end, size;
164
165                         if (md->type != EFI_CONVENTIONAL_MEMORY)
166                                 continue;
167
168                         start = md->phys_addr;
169                         end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
170
171                         if (m_start <= end && m_end >= start)
172                                 /* fake range overlaps descriptor */;
173                         else
174                                 continue;
175
176                         /*
177                          * Trim the boundary of the e820 update to the
178                          * descriptor in case the fake range overlaps
179                          * !EFI_CONVENTIONAL_MEMORY
180                          */
181                         start = max(start, m_start);
182                         end = min(end, m_end);
183                         size = end - start + 1;
184
185                         if (end <= start)
186                                 continue;
187
188                         /*
189                          * Ensure each efi_fake_mem instance results in
190                          * a unique e820 resource
191                          */
192                         e820__range_remove(start, size, E820_TYPE_RAM, 1);
193                         e820__range_add(start, size, E820_TYPE_SOFT_RESERVED);
194                         e820__update_table(e820_table);
195                 }
196         }
197 }