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 GRUB_MOD_LICENSE ("GPLv3+");
31 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
33 struct grub_mmap_region
*grub_mmap_overlays
= 0;
34 static int curhandle
= 1;
38 static int current_priority
= 1;
40 /* Scanline events. */
43 /* At which memory address. */
45 /* 0 = region starts, 1 = region ends. */
47 /* Which type of memory region? */
48 grub_memory_type_t memtype
;
49 /* Priority. 0 means coming from firmware. */
53 /* Context for grub_mmap_iterate. */
54 struct grub_mmap_iterate_ctx
56 struct grub_mmap_scan
*scanline_events
;
60 /* Helper for grub_mmap_iterate. */
62 count_hook (grub_uint64_t addr
__attribute__ ((unused
)),
63 grub_uint64_t size
__attribute__ ((unused
)),
64 grub_memory_type_t type
__attribute__ ((unused
)), void *data
)
72 /* Helper for grub_mmap_iterate. */
74 fill_hook (grub_uint64_t addr
, grub_uint64_t size
, grub_memory_type_t type
,
77 struct grub_mmap_iterate_ctx
*ctx
= data
;
79 if (type
== GRUB_MEMORY_HOLE
)
81 grub_dprintf ("mmap", "Unknown memory type %d. Assuming unusable\n",
83 type
= GRUB_MEMORY_RESERVED
;
86 ctx
->scanline_events
[ctx
->i
].pos
= addr
;
87 ctx
->scanline_events
[ctx
->i
].type
= 0;
88 ctx
->scanline_events
[ctx
->i
].memtype
= type
;
89 ctx
->scanline_events
[ctx
->i
].priority
= 0;
93 ctx
->scanline_events
[ctx
->i
].pos
= addr
+ size
;
94 ctx
->scanline_events
[ctx
->i
].type
= 1;
95 ctx
->scanline_events
[ctx
->i
].memtype
= type
;
96 ctx
->scanline_events
[ctx
->i
].priority
= 0;
104 struct mm_list
*next
;
105 grub_memory_type_t val
;
110 grub_mmap_iterate (grub_memory_hook_t hook
, void *hook_data
)
112 /* This function resolves overlapping regions and sorts the memory map.
113 It uses scanline (sweeping) algorithm.
115 struct grub_mmap_iterate_ctx ctx
;
118 struct grub_mmap_scan t
;
120 /* Previous scanline event. */
121 grub_uint64_t lastaddr
;
123 /* Current scanline event. */
125 /* How many regions of given type/priority overlap at current location? */
126 /* Normally there shouldn't be more than one region per priority but be robust. */
127 struct mm_list
*present
;
128 /* Number of mmap chunks. */
131 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
132 struct grub_mmap_region
*cur
;
137 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
138 for (cur
= grub_mmap_overlays
; cur
; cur
= cur
->next
)
142 grub_machine_mmap_iterate (count_hook
, &mmap_num
);
144 /* Initialize variables. */
145 ctx
.scanline_events
= (struct grub_mmap_scan
*)
146 grub_malloc (sizeof (struct grub_mmap_scan
) * 2 * mmap_num
);
148 present
= grub_zalloc (sizeof (present
[0]) * current_priority
);
150 if (! ctx
.scanline_events
|| !present
)
152 grub_free (ctx
.scanline_events
);
158 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
159 /* Register scanline events. */
160 for (cur
= grub_mmap_overlays
; cur
; cur
= cur
->next
)
162 ctx
.scanline_events
[ctx
.i
].pos
= cur
->start
;
163 ctx
.scanline_events
[ctx
.i
].type
= 0;
164 ctx
.scanline_events
[ctx
.i
].memtype
= cur
->type
;
165 ctx
.scanline_events
[ctx
.i
].priority
= cur
->priority
;
168 ctx
.scanline_events
[ctx
.i
].pos
= cur
->end
;
169 ctx
.scanline_events
[ctx
.i
].type
= 1;
170 ctx
.scanline_events
[ctx
.i
].memtype
= cur
->type
;
171 ctx
.scanline_events
[ctx
.i
].priority
= cur
->priority
;
174 #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
176 grub_machine_mmap_iterate (fill_hook
, &ctx
);
178 /* Primitive bubble sort. It has complexity O(n^2) but since we're
179 unlikely to have more than 100 chunks it's probably one of the
180 fastest for one purpose. */
185 for (i
= 0; i
< 2 * mmap_num
- 1; i
++)
186 if (ctx
.scanline_events
[i
+ 1].pos
< ctx
.scanline_events
[i
].pos
187 || (ctx
.scanline_events
[i
+ 1].pos
== ctx
.scanline_events
[i
].pos
188 && ctx
.scanline_events
[i
+ 1].type
== 0
189 && ctx
.scanline_events
[i
].type
== 1))
191 t
= ctx
.scanline_events
[i
+ 1];
192 ctx
.scanline_events
[i
+ 1] = ctx
.scanline_events
[i
];
193 ctx
.scanline_events
[i
] = t
;
198 lastaddr
= ctx
.scanline_events
[0].pos
;
199 lasttype
= ctx
.scanline_events
[0].memtype
;
200 for (i
= 0; i
< 2 * mmap_num
; i
++)
203 if (ctx
.scanline_events
[i
].type
)
205 if (present
[ctx
.scanline_events
[i
].priority
].present
)
207 if (present
[ctx
.scanline_events
[i
].priority
].val
== ctx
.scanline_events
[i
].memtype
)
209 if (present
[ctx
.scanline_events
[i
].priority
].next
)
211 struct mm_list
*p
= present
[ctx
.scanline_events
[i
].priority
].next
;
212 present
[ctx
.scanline_events
[i
].priority
] = *p
;
217 present
[ctx
.scanline_events
[i
].priority
].present
= 0;
222 struct mm_list
**q
= &(present
[ctx
.scanline_events
[i
].priority
].next
), *p
;
223 for (; *q
; q
= &((*q
)->next
))
224 if ((*q
)->val
== ctx
.scanline_events
[i
].memtype
)
236 if (!present
[ctx
.scanline_events
[i
].priority
].present
)
238 present
[ctx
.scanline_events
[i
].priority
].present
= 1;
239 present
[ctx
.scanline_events
[i
].priority
].val
= ctx
.scanline_events
[i
].memtype
;
243 struct mm_list
*n
= grub_malloc (sizeof (*n
));
244 n
->val
= ctx
.scanline_events
[i
].memtype
;
246 n
->next
= present
[ctx
.scanline_events
[i
].priority
].next
;
247 present
[ctx
.scanline_events
[i
].priority
].next
= n
;
251 /* Determine current region type. */
255 for (k
= current_priority
- 1; k
>= 0; k
--)
256 if (present
[k
].present
)
258 curtype
= present
[k
].val
;
263 /* Announce region to the hook if necessary. */
264 if ((curtype
== -1 || curtype
!= lasttype
)
265 && lastaddr
!= ctx
.scanline_events
[i
].pos
267 && lasttype
!= GRUB_MEMORY_HOLE
268 && hook (lastaddr
, ctx
.scanline_events
[i
].pos
- lastaddr
, lasttype
,
271 grub_free (ctx
.scanline_events
);
272 return GRUB_ERR_NONE
;
275 /* Update last values if necessary. */
276 if (curtype
== -1 || curtype
!= lasttype
)
279 lastaddr
= ctx
.scanline_events
[i
].pos
;
283 grub_free (ctx
.scanline_events
);
284 return GRUB_ERR_NONE
;
287 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
289 grub_mmap_register (grub_uint64_t start
, grub_uint64_t size
, int type
)
291 struct grub_mmap_region
*cur
;
293 grub_dprintf ("mmap", "registering\n");
295 cur
= (struct grub_mmap_region
*)
296 grub_malloc (sizeof (struct grub_mmap_region
));
300 cur
->next
= grub_mmap_overlays
;
302 cur
->end
= start
+ size
;
304 cur
->handle
= curhandle
++;
305 cur
->priority
= current_priority
++;
306 grub_mmap_overlays
= cur
;
308 if (grub_machine_mmap_register (start
, size
, type
, curhandle
))
310 grub_mmap_overlays
= cur
->next
;
319 grub_mmap_unregister (int handle
)
321 struct grub_mmap_region
*cur
, *prev
;
323 for (cur
= grub_mmap_overlays
, prev
= 0; cur
; prev
= cur
, cur
= cur
->next
)
324 if (handle
== cur
->handle
)
327 err
= grub_machine_mmap_unregister (handle
);
332 prev
->next
= cur
->next
;
334 grub_mmap_overlays
= cur
->next
;
336 return GRUB_ERR_NONE
;
338 return grub_error (GRUB_ERR_BUG
, "mmap overlay not found");
341 #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
343 #define CHUNK_SIZE 0x400
345 struct badram_entry
{
346 grub_uint64_t addr
, mask
;
349 static inline grub_uint64_t
350 fill_mask (struct badram_entry
*entry
, grub_uint64_t iterator
)
353 grub_uint64_t ret
= (entry
->addr
& entry
->mask
);
355 /* Find first fixed bit. */
356 for (i
= 0; i
< 64; i
++)
357 if ((entry
->mask
& (1ULL << i
)) != 0)
361 if ((entry
->mask
& (1ULL << i
)) == 0)
363 if ((iterator
& (1ULL << j
)) != 0)
370 /* Helper for grub_cmd_badram. */
372 badram_iter (grub_uint64_t addr
, grub_uint64_t size
,
373 grub_memory_type_t type
__attribute__ ((unused
)), void *data
)
375 struct badram_entry
*entry
= data
;
376 grub_uint64_t iterator
, low
, high
, cur
;
379 grub_dprintf ("badram", "hook %llx+%llx\n", (unsigned long long) addr
,
380 (unsigned long long) size
);
382 /* How many trailing zeros? */
383 for (tail
= 0; ! (entry
->mask
& (1ULL << tail
)); tail
++);
385 /* How many zeros in mask? */
387 for (i
= 0; i
< 64; i
++)
388 if (! (entry
->mask
& (1ULL << i
)))
391 if (fill_mask (entry
, 0) >= addr
)
397 /* Find starting value. Keep low and high such that
398 fill_mask (low) < addr and fill_mask (high) >= addr;
400 while (high
- low
> 1)
402 cur
= (low
+ high
) / 2;
403 if (fill_mask (entry
, cur
) >= addr
)
411 for (; iterator
< (1ULL << (var
- tail
))
412 && (cur
= fill_mask (entry
, iterator
)) < addr
+ size
;
415 grub_dprintf ("badram", "%llx (size %llx) is a badram range\n",
416 (unsigned long long) cur
, (1ULL << tail
));
417 grub_mmap_register (cur
, (1ULL << tail
), GRUB_MEMORY_HOLE
);
423 grub_cmd_badram (grub_command_t cmd
__attribute__ ((unused
)),
424 int argc
, char **args
)
427 struct badram_entry entry
;
430 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("one argument expected"));
432 grub_dprintf ("badram", "executing badram\n");
438 /* Parse address and mask. */
439 entry
.addr
= grub_strtoull (str
, &str
, 16);
442 entry
.mask
= grub_strtoull (str
, &str
, 16);
446 if (grub_errno
== GRUB_ERR_BAD_NUMBER
)
449 return GRUB_ERR_NONE
;
452 /* When part of a page is tainted, we discard the whole of it. There's
453 no point in providing sub-page chunks. */
454 entry
.mask
&= ~(CHUNK_SIZE
- 1);
456 grub_dprintf ("badram", "badram %llx:%llx\n",
457 (unsigned long long) entry
.addr
,
458 (unsigned long long) entry
.mask
);
460 grub_mmap_iterate (badram_iter
, &entry
);
465 parsemem (const char *str
)
470 ret
= grub_strtoul (str
, &ptr
, 0);
486 struct cutmem_range
{
487 grub_uint64_t from
, to
;
490 /* Helper for grub_cmd_cutmem. */
492 cutmem_iter (grub_uint64_t addr
, grub_uint64_t size
,
493 grub_memory_type_t type
__attribute__ ((unused
)), void *data
)
495 struct cutmem_range
*range
= data
;
496 grub_uint64_t end
= addr
+ size
;
498 if (addr
<= range
->from
)
500 if (end
>= range
->to
)
506 grub_mmap_register (addr
, end
- addr
, GRUB_MEMORY_HOLE
);
511 grub_cmd_cutmem (grub_command_t cmd
__attribute__ ((unused
)),
512 int argc
, char **args
)
514 struct cutmem_range range
;
517 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("two arguments expected"));
519 range
.from
= parsemem (args
[0]);
523 range
.to
= parsemem (args
[1]);
527 grub_mmap_iterate (cutmem_iter
, &range
);
529 return GRUB_ERR_NONE
;
532 static grub_command_t cmd
, cmd_cut
;
537 cmd
= grub_register_command ("badram", grub_cmd_badram
,
538 N_("ADDR1,MASK1[,ADDR2,MASK2[,...]]"),
539 N_("Declare memory regions as faulty (badram)."));
540 cmd_cut
= grub_register_command ("cutmem", grub_cmd_cutmem
,
541 N_("FROM[K|M|G] TO[K|M|G]"),
542 N_("Remove any memory regions in specified range."));
548 grub_unregister_command (cmd
);
549 grub_unregister_command (cmd_cut
);