]>
Commit | Line | Data |
---|---|---|
09d842b9 | 1 | /* Mmap management. */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
4 | * Copyright (C) 2009 Free Software Foundation, Inc. | |
5 | * | |
6 | * GRUB is free software: you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation, either version 3 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * GRUB is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include <grub/machine/memory.h> | |
21 | #include <grub/memory.h> | |
22 | #include <grub/err.h> | |
23 | #include <grub/efi/api.h> | |
24 | #include <grub/efi/efi.h> | |
25 | #include <grub/mm.h> | |
26 | #include <grub/misc.h> | |
27 | ||
28 | #define NEXT_MEMORY_DESCRIPTOR(desc, size) \ | |
29 | ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size))) | |
30 | ||
31 | grub_err_t | |
32 | grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, | |
33 | grub_uint64_t, | |
34 | grub_uint32_t)) | |
35 | { | |
36 | grub_efi_uintn_t mmap_size = 0; | |
37 | grub_efi_memory_descriptor_t *map_buf = 0; | |
38 | grub_efi_uintn_t map_key = 0; | |
39 | grub_efi_uintn_t desc_size = 0; | |
40 | grub_efi_uint32_t desc_version = 0; | |
41 | grub_efi_memory_descriptor_t *desc; | |
42 | ||
43 | if (grub_efi_get_memory_map (&mmap_size, map_buf, | |
44 | &map_key, &desc_size, | |
45 | &desc_version) < 0) | |
46 | return grub_errno; | |
47 | ||
48 | map_buf = grub_malloc (mmap_size); | |
49 | if (! map_buf) | |
50 | return grub_errno; | |
51 | ||
52 | if (grub_efi_get_memory_map (&mmap_size, map_buf, | |
53 | &map_key, &desc_size, | |
54 | &desc_version) <= 0) | |
55 | { | |
56 | grub_free (map_buf); | |
57 | return grub_errno; | |
58 | } | |
59 | ||
60 | for (desc = map_buf; | |
61 | desc < NEXT_MEMORY_DESCRIPTOR (map_buf, mmap_size); | |
62 | desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) | |
63 | { | |
64 | grub_dprintf ("mmap", "EFI memory region 0x%llx-0x%llx: %d\n", | |
65 | (unsigned long long) desc->physical_start, | |
66 | (unsigned long long) desc->physical_start | |
67 | + desc->num_pages * 4096, desc->type); | |
68 | switch (desc->type) | |
69 | { | |
70 | case GRUB_EFI_RUNTIME_SERVICES_CODE: | |
71 | hook (desc->physical_start, desc->num_pages * 4096, | |
72 | GRUB_MACHINE_MEMORY_CODE); | |
73 | break; | |
74 | ||
75 | default: | |
76 | grub_printf ("Unknown memory type %d, considering reserved\n", | |
77 | desc->type); | |
78 | ||
79 | case GRUB_EFI_RESERVED_MEMORY_TYPE: | |
80 | case GRUB_EFI_RUNTIME_SERVICES_DATA: | |
81 | case GRUB_EFI_UNUSABLE_MEMORY: | |
82 | case GRUB_EFI_MEMORY_MAPPED_IO: | |
83 | case GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE: | |
84 | case GRUB_EFI_PAL_CODE: | |
85 | hook (desc->physical_start, desc->num_pages * 4096, | |
86 | GRUB_MACHINE_MEMORY_RESERVED); | |
87 | break; | |
88 | ||
89 | case GRUB_EFI_LOADER_CODE: | |
90 | case GRUB_EFI_LOADER_DATA: | |
91 | case GRUB_EFI_BOOT_SERVICES_CODE: | |
92 | case GRUB_EFI_BOOT_SERVICES_DATA: | |
93 | case GRUB_EFI_CONVENTIONAL_MEMORY: | |
94 | hook (desc->physical_start, desc->num_pages * 4096, | |
95 | GRUB_MACHINE_MEMORY_AVAILABLE); | |
96 | break; | |
97 | ||
98 | case GRUB_EFI_ACPI_RECLAIM_MEMORY: | |
99 | hook (desc->physical_start, desc->num_pages * 4096, | |
100 | GRUB_MACHINE_MEMORY_ACPI); | |
101 | break; | |
102 | ||
103 | case GRUB_EFI_ACPI_MEMORY_NVS: | |
104 | hook (desc->physical_start, desc->num_pages * 4096, | |
105 | GRUB_MACHINE_MEMORY_NVS); | |
106 | break; | |
107 | } | |
108 | } | |
109 | ||
110 | return GRUB_ERR_NONE; | |
111 | } | |
112 | ||
113 | static inline grub_efi_memory_type_t | |
114 | make_efi_memtype (int type) | |
115 | { | |
116 | switch (type) | |
117 | { | |
118 | case GRUB_MACHINE_MEMORY_CODE: | |
119 | return GRUB_EFI_RUNTIME_SERVICES_CODE; | |
120 | ||
121 | /* No way to remove a chunk of memory from EFI mmap. | |
122 | So mark it as unusable. */ | |
123 | case GRUB_MACHINE_MEMORY_HOLE: | |
124 | ||
125 | default: | |
126 | ||
127 | case GRUB_MACHINE_MEMORY_RESERVED: | |
128 | return GRUB_EFI_UNUSABLE_MEMORY; | |
129 | ||
130 | case GRUB_MACHINE_MEMORY_AVAILABLE: | |
131 | return GRUB_EFI_CONVENTIONAL_MEMORY; | |
132 | ||
133 | case GRUB_MACHINE_MEMORY_ACPI: | |
134 | return GRUB_EFI_ACPI_RECLAIM_MEMORY; | |
135 | ||
136 | case GRUB_MACHINE_MEMORY_NVS: | |
137 | return GRUB_EFI_ACPI_RECLAIM_MEMORY; | |
138 | ||
139 | } | |
140 | ||
141 | } | |
142 | ||
143 | struct overlay | |
144 | { | |
145 | struct overlay *next; | |
146 | grub_efi_physical_address_t address; | |
147 | grub_efi_uintn_t pages; | |
148 | int handle; | |
149 | }; | |
150 | ||
151 | static struct overlay *overlays = 0; | |
152 | static int curhandle = 1; | |
153 | ||
154 | int | |
155 | grub_mmap_register (grub_uint64_t start, grub_uint64_t size, int type) | |
156 | { | |
157 | grub_uint64_t end = start + size; | |
158 | grub_efi_physical_address_t address; | |
159 | grub_efi_boot_services_t *b; | |
160 | grub_efi_uintn_t pages; | |
161 | grub_efi_status_t status; | |
162 | struct overlay *curover; | |
163 | ||
164 | curover = (struct overlay *) grub_malloc (sizeof (struct overlay)); | |
165 | if (! curover) | |
166 | return 0; | |
167 | ||
168 | b = grub_efi_system_table->boot_services; | |
169 | address = start & (~0x3ffULL); | |
170 | pages = (end - address + 0x3ff) >> 12; | |
171 | status = efi_call_2 (b->free_pages, address, pages); | |
172 | if (status != GRUB_EFI_SUCCESS && status != GRUB_EFI_NOT_FOUND) | |
173 | { | |
174 | grub_free (curover); | |
175 | return 0; | |
176 | } | |
177 | status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ADDRESS, | |
178 | make_efi_memtype (type), pages, &address); | |
179 | if (status != GRUB_EFI_SUCCESS) | |
180 | { | |
181 | grub_free (curover); | |
182 | return 0; | |
183 | } | |
184 | curover->next = overlays; | |
185 | curover->handle = curhandle++; | |
186 | curover->address = address; | |
187 | curover->pages = pages; | |
188 | overlays = curover; | |
189 | ||
190 | return curover->handle; | |
191 | } | |
192 | ||
193 | grub_err_t | |
28a85665 | 194 | grub_mmap_unregister (int handle) |
09d842b9 | 195 | { |
196 | struct overlay *curover, *prevover; | |
197 | grub_efi_boot_services_t *b; | |
198 | grub_efi_status_t status; | |
199 | ||
200 | b = grub_efi_system_table->boot_services; | |
201 | ||
202 | ||
203 | for (curover = overlays, prevover = 0; curover; | |
204 | prevover = curover, curover = curover->next) | |
205 | { | |
206 | if (curover->handle == handle) | |
207 | { | |
208 | status = efi_call_2 (b->free_pages, curover->address, curover->pages); | |
209 | if (prevover != 0) | |
210 | prevover->next = curover->next; | |
211 | else | |
212 | overlays = curover->next; | |
213 | grub_free (curover); | |
214 | return GRUB_ERR_NONE; | |
215 | } | |
216 | } | |
217 | return grub_error (GRUB_ERR_BAD_ARGUMENT, "handle %d not found", handle); | |
218 | } | |
219 | ||
220 | /* Result is always page-aligned. */ | |
2fee74f1 | 221 | void * |
09d842b9 | 222 | grub_mmap_malign_and_register (grub_uint64_t align __attribute__ ((unused)), |
223 | grub_uint64_t size, | |
224 | int *handle, int type, | |
225 | int flags __attribute__ ((unused))) | |
226 | { | |
227 | grub_efi_physical_address_t address; | |
228 | grub_efi_boot_services_t *b; | |
229 | grub_efi_uintn_t pages; | |
230 | grub_efi_status_t status; | |
231 | struct overlay *curover; | |
232 | grub_efi_allocate_type_t atype; | |
233 | ||
234 | curover = (struct overlay *) grub_malloc (sizeof (struct overlay)); | |
235 | if (! curover) | |
236 | return 0; | |
237 | ||
238 | b = grub_efi_system_table->boot_services; | |
239 | ||
240 | address = 0xffffffff; | |
241 | ||
242 | #if GRUB_TARGET_SIZEOF_VOID_P < 8 | |
243 | /* Limit the memory access to less than 4GB for 32-bit platforms. */ | |
244 | atype = GRUB_EFI_ALLOCATE_MAX_ADDRESS; | |
245 | #else | |
246 | atype = GRUB_EFI_ALLOCATE_ANY_PAGES; | |
247 | #endif | |
248 | ||
249 | pages = (size + 0x3ff) >> 12; | |
250 | status = efi_call_4 (b->allocate_pages, atype, | |
251 | make_efi_memtype (type), pages, &address); | |
252 | if (status != GRUB_EFI_SUCCESS) | |
253 | { | |
254 | grub_free (curover); | |
255 | return 0; | |
256 | } | |
257 | ||
258 | if (address == 0) | |
259 | { | |
260 | /* Uggh, the address 0 was allocated... This is too annoying, | |
261 | so reallocate another one. */ | |
262 | address = 0xffffffff; | |
263 | status = efi_call_4 (b->allocate_pages, atype, | |
264 | make_efi_memtype (type), pages, &address); | |
265 | grub_efi_free_pages (0, pages); | |
266 | if (status != GRUB_EFI_SUCCESS) | |
267 | return 0; | |
268 | } | |
269 | ||
270 | curover->next = overlays; | |
271 | curover->handle = curhandle++; | |
272 | curover->address = address; | |
273 | curover->pages = pages; | |
274 | overlays = curover; | |
275 | *handle = curover->handle; | |
276 | ||
277 | return UINT_TO_PTR (curover->address); | |
278 | } | |
279 | ||
280 | void | |
281 | grub_mmap_free_and_unregister (int handle) | |
282 | { | |
28a85665 | 283 | grub_mmap_unregister (handle); |
09d842b9 | 284 | } |