xref: /linux/arch/x86/platform/efi/memmap.c (revision 87ce9e83ab8be5daf64351cd481ffa6537778e6b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Common EFI memory map functions.
4  */
5 
6 #define pr_fmt(fmt) "efi: " fmt
7 
8 #include <linux/init.h>
9 #include <linux/kernel.h>
10 #include <linux/efi.h>
11 #include <linux/io.h>
12 #include <asm/early_ioremap.h>
13 #include <asm/efi.h>
14 #include <linux/memblock.h>
15 #include <linux/slab.h>
16 
17 static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size)
18 {
19 	return memblock_phys_alloc(size, SMP_CACHE_BYTES);
20 }
21 
22 static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size)
23 {
24 	unsigned int order = get_order(size);
25 	struct page *p = alloc_pages(GFP_KERNEL, order);
26 
27 	if (!p)
28 		return 0;
29 
30 	return PFN_PHYS(page_to_pfn(p));
31 }
32 
33 static
34 void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags)
35 {
36 	if (flags & EFI_MEMMAP_MEMBLOCK) {
37 		memblock_phys_free(phys, size);
38 	} else if (flags & EFI_MEMMAP_SLAB) {
39 		struct page *p = pfn_to_page(PHYS_PFN(phys));
40 		unsigned int order = get_order(size);
41 
42 		__free_pages(p, order);
43 	}
44 }
45 
46 /**
47  * efi_memmap_alloc - Allocate memory for the EFI memory map
48  * @num_entries: Number of entries in the allocated map.
49  * @data: efi memmap installation parameters
50  *
51  * Depending on whether mm_init() has already been invoked or not,
52  * either memblock or "normal" page allocation is used.
53  *
54  * Returns zero on success, a negative error code on failure.
55  */
56 int __init efi_memmap_alloc(unsigned int num_entries,
57 		struct efi_memory_map_data *data)
58 {
59 	/* Expect allocation parameters are zero initialized */
60 	WARN_ON(data->phys_map || data->size);
61 
62 	data->size = num_entries * efi.memmap.desc_size;
63 	data->desc_version = efi.memmap.desc_version;
64 	data->desc_size = efi.memmap.desc_size;
65 	data->flags &= ~(EFI_MEMMAP_SLAB | EFI_MEMMAP_MEMBLOCK);
66 	data->flags |= efi.memmap.flags & EFI_MEMMAP_LATE;
67 
68 	if (slab_is_available()) {
69 		data->flags |= EFI_MEMMAP_SLAB;
70 		data->phys_map = __efi_memmap_alloc_late(data->size);
71 	} else {
72 		data->flags |= EFI_MEMMAP_MEMBLOCK;
73 		data->phys_map = __efi_memmap_alloc_early(data->size);
74 	}
75 
76 	if (!data->phys_map)
77 		return -ENOMEM;
78 	return 0;
79 }
80 
81 /**
82  * efi_memmap_install - Install a new EFI memory map in efi.memmap
83  * @data: efi memmap installation parameters
84  *
85  * Unlike efi_memmap_init_*(), this function does not allow the caller
86  * to switch from early to late mappings. It simply uses the existing
87  * mapping function and installs the new memmap.
88  *
89  * Returns zero on success, a negative error code on failure.
90  */
91 int __init efi_memmap_install(struct efi_memory_map_data *data)
92 {
93 	unsigned long size = efi.memmap.desc_size * efi.memmap.nr_map;
94 	unsigned long flags = efi.memmap.flags;
95 	u64 phys = efi.memmap.phys_map;
96 	int ret;
97 
98 	efi_memmap_unmap();
99 
100 	if (efi_enabled(EFI_PARAVIRT))
101 		return 0;
102 
103 	ret = __efi_memmap_init(data);
104 	if (ret)
105 		return ret;
106 
107 	__efi_memmap_free(phys, size, flags);
108 	return 0;
109 }
110 
111 /**
112  * efi_memmap_split_count - Count number of additional EFI memmap entries
113  * @md: EFI memory descriptor to split
114  * @range: Address range (start, end) to split around
115  *
116  * Returns the number of additional EFI memmap entries required to
117  * accommodate @range.
118  */
119 int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range)
120 {
121 	u64 m_start, m_end;
122 	u64 start, end;
123 	int count = 0;
124 
125 	start = md->phys_addr;
126 	end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
127 
128 	/* modifying range */
129 	m_start = range->start;
130 	m_end = range->end;
131 
132 	if (m_start <= start) {
133 		/* split into 2 parts */
134 		if (start < m_end && m_end < end)
135 			count++;
136 	}
137 
138 	if (start < m_start && m_start < end) {
139 		/* split into 3 parts */
140 		if (m_end < end)
141 			count += 2;
142 		/* split into 2 parts */
143 		if (end <= m_end)
144 			count++;
145 	}
146 
147 	return count;
148 }
149 
150 /**
151  * efi_memmap_insert - Insert a memory region in an EFI memmap
152  * @old_memmap: The existing EFI memory map structure
153  * @buf: Address of buffer to store new map
154  * @mem: Memory map entry to insert
155  *
156  * It is suggested that you call efi_memmap_split_count() first
157  * to see how large @buf needs to be.
158  */
159 void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf,
160 			      struct efi_mem_range *mem)
161 {
162 	u64 m_start, m_end, m_attr;
163 	efi_memory_desc_t *md;
164 	u64 start, end;
165 	void *old, *new;
166 
167 	/* modifying range */
168 	m_start = mem->range.start;
169 	m_end = mem->range.end;
170 	m_attr = mem->attribute;
171 
172 	/*
173 	 * The EFI memory map deals with regions in EFI_PAGE_SIZE
174 	 * units. Ensure that the region described by 'mem' is aligned
175 	 * correctly.
176 	 */
177 	if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) ||
178 	    !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) {
179 		WARN_ON(1);
180 		return;
181 	}
182 
183 	for (old = old_memmap->map, new = buf;
184 	     old < old_memmap->map_end;
185 	     old += old_memmap->desc_size, new += old_memmap->desc_size) {
186 
187 		/* copy original EFI memory descriptor */
188 		memcpy(new, old, old_memmap->desc_size);
189 		md = new;
190 		start = md->phys_addr;
191 		end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
192 
193 		if (m_start <= start && end <= m_end)
194 			md->attribute |= m_attr;
195 
196 		if (m_start <= start &&
197 		    (start < m_end && m_end < end)) {
198 			/* first part */
199 			md->attribute |= m_attr;
200 			md->num_pages = (m_end - md->phys_addr + 1) >>
201 				EFI_PAGE_SHIFT;
202 			/* latter part */
203 			new += old_memmap->desc_size;
204 			memcpy(new, old, old_memmap->desc_size);
205 			md = new;
206 			md->phys_addr = m_end + 1;
207 			md->num_pages = (end - md->phys_addr + 1) >>
208 				EFI_PAGE_SHIFT;
209 		}
210 
211 		if ((start < m_start && m_start < end) && m_end < end) {
212 			/* first part */
213 			md->num_pages = (m_start - md->phys_addr) >>
214 				EFI_PAGE_SHIFT;
215 			/* middle part */
216 			new += old_memmap->desc_size;
217 			memcpy(new, old, old_memmap->desc_size);
218 			md = new;
219 			md->attribute |= m_attr;
220 			md->phys_addr = m_start;
221 			md->num_pages = (m_end - m_start + 1) >>
222 				EFI_PAGE_SHIFT;
223 			/* last part */
224 			new += old_memmap->desc_size;
225 			memcpy(new, old, old_memmap->desc_size);
226 			md = new;
227 			md->phys_addr = m_end + 1;
228 			md->num_pages = (end - m_end) >>
229 				EFI_PAGE_SHIFT;
230 		}
231 
232 		if ((start < m_start && m_start < end) &&
233 		    (end <= m_end)) {
234 			/* first part */
235 			md->num_pages = (m_start - md->phys_addr) >>
236 				EFI_PAGE_SHIFT;
237 			/* latter part */
238 			new += old_memmap->desc_size;
239 			memcpy(new, old, old_memmap->desc_size);
240 			md = new;
241 			md->phys_addr = m_start;
242 			md->num_pages = (end - md->phys_addr + 1) >>
243 				EFI_PAGE_SHIFT;
244 			md->attribute |= m_attr;
245 		}
246 	}
247 }
248