]>
git.proxmox.com Git - grub2.git/blob - grub-core/mmap/mmap.c
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/memory.h>
21 #include <grub/machine/memory.h>
23 #include <grub/misc.h>
25 #include <grub/command.h>
27 #include <grub/i18n.h>
29 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
31 struct grub_mmap_region
*grub_mmap_overlays
= 0;
32 static int curhandle
= 1;
37 grub_mmap_iterate (grub_memory_hook_t hook
)
40 /* This function resolves overlapping regions and sorts the memory map.
41 It uses scanline (sweeping) algorithm.
43 /* If same page is used by multiple types it's resolved
44 according to priority:
46 2 - memory usable by firmware-aware code
48 4 - a range deliberately empty
52 [GRUB_MEMORY_AVAILABLE
] = 1,
53 [GRUB_MEMORY_RESERVED
] = 3,
54 [GRUB_MEMORY_ACPI
] = 2,
55 [GRUB_MEMORY_CODE
] = 3,
56 [GRUB_MEMORY_NVS
] = 3,
57 [GRUB_MEMORY_HOLE
] = 4,
62 /* Scanline events. */
65 /* At which memory address. */
67 /* 0 = region starts, 1 = region ends. */
69 /* Which type of memory region? */
72 struct grub_mmap_scan
*scanline_events
;
73 struct grub_mmap_scan t
;
75 /* Previous scanline event. */
76 grub_uint64_t lastaddr
;
78 /* Current scanline event. */
80 /* How many regions of given type overlap at current location? */
81 int present
[ARRAY_SIZE (priority
)];
82 /* Number of mmap chunks. */
85 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
86 struct grub_mmap_region
*cur
;
89 auto int NESTED_FUNC_ATTR
count_hook (grub_uint64_t
, grub_uint64_t
,
91 int NESTED_FUNC_ATTR
count_hook (grub_uint64_t addr
__attribute__ ((unused
)),
92 grub_uint64_t size
__attribute__ ((unused
)),
93 grub_memory_type_t type
__attribute__ ((unused
)))
99 auto int NESTED_FUNC_ATTR
fill_hook (grub_uint64_t
, grub_uint64_t
,
101 int NESTED_FUNC_ATTR
fill_hook (grub_uint64_t addr
,
103 grub_memory_type_t type
)
105 scanline_events
[i
].pos
= addr
;
106 scanline_events
[i
].type
= 0;
107 if (type
< ARRAY_SIZE (priority
) && priority
[type
])
108 scanline_events
[i
].memtype
= type
;
111 grub_dprintf ("mmap", "Unknown memory type %d. Assuming unusable\n",
113 scanline_events
[i
].memtype
= GRUB_MEMORY_RESERVED
;
117 scanline_events
[i
].pos
= addr
+ size
;
118 scanline_events
[i
].type
= 1;
119 scanline_events
[i
].memtype
= scanline_events
[i
- 1].memtype
;
127 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
128 for (cur
= grub_mmap_overlays
; cur
; cur
= cur
->next
)
132 grub_machine_mmap_iterate (count_hook
);
134 /* Initialize variables. */
135 grub_memset (present
, 0, sizeof (present
));
136 scanline_events
= (struct grub_mmap_scan
*)
137 grub_malloc (sizeof (struct grub_mmap_scan
) * 2 * mmap_num
);
139 if (! scanline_events
)
141 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
142 "couldn't allocate space for new memory map");
146 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
147 /* Register scanline events. */
148 for (cur
= grub_mmap_overlays
; cur
; cur
= cur
->next
)
150 scanline_events
[i
].pos
= cur
->start
;
151 scanline_events
[i
].type
= 0;
152 if (cur
->type
< ARRAY_SIZE (priority
) && priority
[cur
->type
])
153 scanline_events
[i
].memtype
= cur
->type
;
155 scanline_events
[i
].memtype
= GRUB_MEMORY_RESERVED
;
158 scanline_events
[i
].pos
= cur
->end
;
159 scanline_events
[i
].type
= 1;
160 scanline_events
[i
].memtype
= scanline_events
[i
- 1].memtype
;
163 #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
165 grub_machine_mmap_iterate (fill_hook
);
167 /* Primitive bubble sort. It has complexity O(n^2) but since we're
168 unlikely to have more than 100 chunks it's probably one of the
169 fastest for one purpose. */
174 for (i
= 0; i
< 2 * mmap_num
- 1; i
++)
175 if (scanline_events
[i
+ 1].pos
< scanline_events
[i
].pos
176 || (scanline_events
[i
+ 1].pos
== scanline_events
[i
].pos
177 && scanline_events
[i
+ 1].type
== 0
178 && scanline_events
[i
].type
== 1))
180 t
= scanline_events
[i
+ 1];
181 scanline_events
[i
+ 1] = scanline_events
[i
];
182 scanline_events
[i
] = t
;
187 lastaddr
= scanline_events
[0].pos
;
188 lasttype
= scanline_events
[0].memtype
;
189 for (i
= 0; i
< 2 * mmap_num
; i
++)
193 if (scanline_events
[i
].type
)
194 present
[scanline_events
[i
].memtype
]--;
196 present
[scanline_events
[i
].memtype
]++;
198 /* Determine current region type. */
200 for (k
= 0; k
< ARRAY_SIZE (priority
); k
++)
201 if (present
[k
] && (curtype
== -1 || priority
[k
] > priority
[curtype
]))
204 /* Announce region to the hook if necessary. */
205 if ((curtype
== -1 || curtype
!= lasttype
)
206 && lastaddr
!= scanline_events
[i
].pos
208 && lasttype
!= GRUB_MEMORY_HOLE
209 && hook (lastaddr
, scanline_events
[i
].pos
- lastaddr
, lasttype
))
211 grub_free (scanline_events
);
212 return GRUB_ERR_NONE
;
215 /* Update last values if necessary. */
216 if (curtype
== -1 || curtype
!= lasttype
)
219 lastaddr
= scanline_events
[i
].pos
;
223 grub_free (scanline_events
);
224 return GRUB_ERR_NONE
;
227 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
229 grub_mmap_register (grub_uint64_t start
, grub_uint64_t size
, int type
)
231 struct grub_mmap_region
*cur
;
233 grub_dprintf ("mmap", "registering\n");
235 cur
= (struct grub_mmap_region
*)
236 grub_malloc (sizeof (struct grub_mmap_region
));
239 grub_error (GRUB_ERR_OUT_OF_MEMORY
,
240 "couldn't allocate memory map overlay");
244 cur
->next
= grub_mmap_overlays
;
246 cur
->end
= start
+ size
;
248 cur
->handle
= curhandle
++;
249 grub_mmap_overlays
= cur
;
251 if (grub_machine_mmap_register (start
, size
, type
, curhandle
))
253 grub_mmap_overlays
= cur
->next
;
262 grub_mmap_unregister (int handle
)
264 struct grub_mmap_region
*cur
, *prev
;
266 for (cur
= grub_mmap_overlays
, prev
= 0; cur
; prev
= cur
, cur
= cur
->next
)
267 if (handle
== cur
->handle
)
270 if ((err
= grub_machine_mmap_unregister (handle
)))
274 prev
->next
= cur
->next
;
276 grub_mmap_overlays
= cur
->next
;
278 return GRUB_ERR_NONE
;
280 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "mmap overlay not found");
283 #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
285 #define CHUNK_SIZE 0x400
287 static inline grub_uint64_t
288 fill_mask (grub_uint64_t addr
, grub_uint64_t mask
, grub_uint64_t iterator
)
291 grub_uint64_t ret
= (addr
& mask
);
293 /* Find first fixed bit. */
294 for (i
= 0; i
< 64; i
++)
295 if ((mask
& (1ULL << i
)) != 0)
299 if ((mask
& (1ULL << i
)) == 0)
301 if ((iterator
& (1ULL << j
)) != 0)
309 grub_cmd_badram (grub_command_t cmd
__attribute__ ((unused
)),
310 int argc
, char **args
)
313 grub_uint64_t badaddr
, badmask
;
315 auto int NESTED_FUNC_ATTR
hook (grub_uint64_t
, grub_uint64_t
,
317 int NESTED_FUNC_ATTR
hook (grub_uint64_t addr
,
319 grub_memory_type_t type
__attribute__ ((unused
)))
321 grub_uint64_t iterator
, low
, high
, cur
;
324 grub_dprintf ("badram", "hook %llx+%llx\n", (unsigned long long) addr
,
325 (unsigned long long) size
);
327 /* How many trailing zeros? */
328 for (tail
= 0; ! (badmask
& (1ULL << tail
)); tail
++);
330 /* How many zeros in mask? */
332 for (i
= 0; i
< 64; i
++)
333 if (! (badmask
& (1ULL << i
)))
336 if (fill_mask (badaddr
, badmask
, 0) >= addr
)
342 /* Find starting value. Keep low and high such that
343 fill_mask (low) < addr and fill_mask (high) >= addr;
345 while (high
- low
> 1)
347 cur
= (low
+ high
) / 2;
348 if (fill_mask (badaddr
, badmask
, cur
) >= addr
)
356 for (; iterator
< (1ULL << (var
- tail
))
357 && (cur
= fill_mask (badaddr
, badmask
, iterator
)) < addr
+ size
;
360 grub_dprintf ("badram", "%llx (size %llx) is a badram range\n",
361 (unsigned long long) cur
, (1ULL << tail
));
362 grub_mmap_register (cur
, (1ULL << tail
), GRUB_MEMORY_HOLE
);
368 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "badram string required");
370 grub_dprintf ("badram", "executing badram\n");
376 /* Parse address and mask. */
377 badaddr
= grub_strtoull (str
, &str
, 16);
380 badmask
= grub_strtoull (str
, &str
, 16);
384 if (grub_errno
== GRUB_ERR_BAD_NUMBER
)
387 return GRUB_ERR_NONE
;
390 /* When part of a page is tainted, we discard the whole of it. There's
391 no point in providing sub-page chunks. */
392 badmask
&= ~(CHUNK_SIZE
- 1);
394 grub_dprintf ("badram", "badram %llx:%llx\n",
395 (unsigned long long) badaddr
, (unsigned long long) badmask
);
397 grub_mmap_iterate (hook
);
402 grub_cmd_cutmem (grub_command_t cmd
__attribute__ ((unused
)),
403 int argc
, char **args
)
405 grub_uint64_t cutmem
;
408 auto int NESTED_FUNC_ATTR
hook (grub_uint64_t
, grub_uint64_t
,
410 int NESTED_FUNC_ATTR
hook (grub_uint64_t addr
,
412 grub_memory_type_t type
__attribute__ ((unused
)))
414 grub_uint64_t end
= addr
+ size
;
421 grub_mmap_register (addr
, end
- addr
, GRUB_MEMORY_HOLE
);
426 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "number required");
428 cutmem
= grub_strtoul (args
[0], &ptr
, 0);
446 grub_mmap_iterate (hook
);
448 return GRUB_ERR_NONE
;
451 static grub_command_t cmd
, cmd_cut
;
456 cmd
= grub_register_command ("badram", grub_cmd_badram
,
457 N_("ADDR1,MASK1[,ADDR2,MASK2[,...]]"),
458 N_("Declare memory regions as badram."));
459 cmd_cut
= grub_register_command ("cutmem", grub_cmd_cutmem
,
461 N_("Remove any memory regions above ADDR."));
467 grub_unregister_command (cmd
);
468 grub_unregister_command (cmd_cut
);