]>
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 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;
39 grub_mmap_iterate (grub_memory_hook_t hook
)
42 /* This function resolves overlapping regions and sorts the memory map.
43 It uses scanline (sweeping) algorithm.
45 /* If same page is used by multiple types it's resolved
46 according to priority:
48 2 - memory usable by firmware-aware code
50 4 - a range deliberately empty
54 [GRUB_MEMORY_AVAILABLE
] = 1,
55 [GRUB_MEMORY_RESERVED
] = 3,
56 [GRUB_MEMORY_ACPI
] = 2,
57 [GRUB_MEMORY_CODE
] = 3,
58 [GRUB_MEMORY_NVS
] = 3,
59 [GRUB_MEMORY_HOLE
] = 4,
64 /* Scanline events. */
67 /* At which memory address. */
69 /* 0 = region starts, 1 = region ends. */
71 /* Which type of memory region? */
74 struct grub_mmap_scan
*scanline_events
;
75 struct grub_mmap_scan t
;
77 /* Previous scanline event. */
78 grub_uint64_t lastaddr
;
80 /* Current scanline event. */
82 /* How many regions of given type overlap at current location? */
83 int present
[ARRAY_SIZE (priority
)];
84 /* Number of mmap chunks. */
87 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
88 struct grub_mmap_region
*cur
;
91 auto int NESTED_FUNC_ATTR
count_hook (grub_uint64_t
, grub_uint64_t
,
93 int NESTED_FUNC_ATTR
count_hook (grub_uint64_t addr
__attribute__ ((unused
)),
94 grub_uint64_t size
__attribute__ ((unused
)),
95 grub_memory_type_t type
__attribute__ ((unused
)))
101 auto int NESTED_FUNC_ATTR
fill_hook (grub_uint64_t
, grub_uint64_t
,
103 int NESTED_FUNC_ATTR
fill_hook (grub_uint64_t addr
,
105 grub_memory_type_t type
)
107 scanline_events
[i
].pos
= addr
;
108 scanline_events
[i
].type
= 0;
109 if (type
< ARRAY_SIZE (priority
) && priority
[type
])
110 scanline_events
[i
].memtype
= type
;
113 grub_dprintf ("mmap", "Unknown memory type %d. Assuming unusable\n",
115 scanline_events
[i
].memtype
= GRUB_MEMORY_RESERVED
;
119 scanline_events
[i
].pos
= addr
+ size
;
120 scanline_events
[i
].type
= 1;
121 scanline_events
[i
].memtype
= scanline_events
[i
- 1].memtype
;
129 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
130 for (cur
= grub_mmap_overlays
; cur
; cur
= cur
->next
)
134 grub_machine_mmap_iterate (count_hook
);
136 /* Initialize variables. */
137 grub_memset (present
, 0, sizeof (present
));
138 scanline_events
= (struct grub_mmap_scan
*)
139 grub_malloc (sizeof (struct grub_mmap_scan
) * 2 * mmap_num
);
141 if (! scanline_events
)
143 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
144 "couldn't allocate space for new memory map");
148 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
149 /* Register scanline events. */
150 for (cur
= grub_mmap_overlays
; cur
; cur
= cur
->next
)
152 scanline_events
[i
].pos
= cur
->start
;
153 scanline_events
[i
].type
= 0;
154 if (cur
->type
< ARRAY_SIZE (priority
) && priority
[cur
->type
])
155 scanline_events
[i
].memtype
= cur
->type
;
157 scanline_events
[i
].memtype
= GRUB_MEMORY_RESERVED
;
160 scanline_events
[i
].pos
= cur
->end
;
161 scanline_events
[i
].type
= 1;
162 scanline_events
[i
].memtype
= scanline_events
[i
- 1].memtype
;
165 #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
167 grub_machine_mmap_iterate (fill_hook
);
169 /* Primitive bubble sort. It has complexity O(n^2) but since we're
170 unlikely to have more than 100 chunks it's probably one of the
171 fastest for one purpose. */
176 for (i
= 0; i
< 2 * mmap_num
- 1; i
++)
177 if (scanline_events
[i
+ 1].pos
< scanline_events
[i
].pos
178 || (scanline_events
[i
+ 1].pos
== scanline_events
[i
].pos
179 && scanline_events
[i
+ 1].type
== 0
180 && scanline_events
[i
].type
== 1))
182 t
= scanline_events
[i
+ 1];
183 scanline_events
[i
+ 1] = scanline_events
[i
];
184 scanline_events
[i
] = t
;
189 lastaddr
= scanline_events
[0].pos
;
190 lasttype
= scanline_events
[0].memtype
;
191 for (i
= 0; i
< 2 * mmap_num
; i
++)
195 if (scanline_events
[i
].type
)
196 present
[scanline_events
[i
].memtype
]--;
198 present
[scanline_events
[i
].memtype
]++;
200 /* Determine current region type. */
202 for (k
= 0; k
< ARRAY_SIZE (priority
); k
++)
203 if (present
[k
] && (curtype
== -1 || priority
[k
] > priority
[curtype
]))
206 /* Announce region to the hook if necessary. */
207 if ((curtype
== -1 || curtype
!= lasttype
)
208 && lastaddr
!= scanline_events
[i
].pos
210 && lasttype
!= GRUB_MEMORY_HOLE
211 && hook (lastaddr
, scanline_events
[i
].pos
- lastaddr
, lasttype
))
213 grub_free (scanline_events
);
214 return GRUB_ERR_NONE
;
217 /* Update last values if necessary. */
218 if (curtype
== -1 || curtype
!= lasttype
)
221 lastaddr
= scanline_events
[i
].pos
;
225 grub_free (scanline_events
);
226 return GRUB_ERR_NONE
;
229 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
231 grub_mmap_register (grub_uint64_t start
, grub_uint64_t size
, int type
)
233 struct grub_mmap_region
*cur
;
235 grub_dprintf ("mmap", "registering\n");
237 cur
= (struct grub_mmap_region
*)
238 grub_malloc (sizeof (struct grub_mmap_region
));
241 grub_error (GRUB_ERR_OUT_OF_MEMORY
,
242 "couldn't allocate memory map overlay");
246 cur
->next
= grub_mmap_overlays
;
248 cur
->end
= start
+ size
;
250 cur
->handle
= curhandle
++;
251 grub_mmap_overlays
= cur
;
253 if (grub_machine_mmap_register (start
, size
, type
, curhandle
))
255 grub_mmap_overlays
= cur
->next
;
264 grub_mmap_unregister (int handle
)
266 struct grub_mmap_region
*cur
, *prev
;
268 for (cur
= grub_mmap_overlays
, prev
= 0; cur
; prev
= cur
, cur
= cur
->next
)
269 if (handle
== cur
->handle
)
272 if ((err
= grub_machine_mmap_unregister (handle
)))
276 prev
->next
= cur
->next
;
278 grub_mmap_overlays
= cur
->next
;
280 return GRUB_ERR_NONE
;
282 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "mmap overlay not found");
285 #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
287 #define CHUNK_SIZE 0x400
289 static inline grub_uint64_t
290 fill_mask (grub_uint64_t addr
, grub_uint64_t mask
, grub_uint64_t iterator
)
293 grub_uint64_t ret
= (addr
& mask
);
295 /* Find first fixed bit. */
296 for (i
= 0; i
< 64; i
++)
297 if ((mask
& (1ULL << i
)) != 0)
301 if ((mask
& (1ULL << i
)) == 0)
303 if ((iterator
& (1ULL << j
)) != 0)
311 grub_cmd_badram (grub_command_t cmd
__attribute__ ((unused
)),
312 int argc
, char **args
)
315 grub_uint64_t badaddr
, badmask
;
317 auto int NESTED_FUNC_ATTR
hook (grub_uint64_t
, grub_uint64_t
,
319 int NESTED_FUNC_ATTR
hook (grub_uint64_t addr
,
321 grub_memory_type_t type
__attribute__ ((unused
)))
323 grub_uint64_t iterator
, low
, high
, cur
;
326 grub_dprintf ("badram", "hook %llx+%llx\n", (unsigned long long) addr
,
327 (unsigned long long) size
);
329 /* How many trailing zeros? */
330 for (tail
= 0; ! (badmask
& (1ULL << tail
)); tail
++);
332 /* How many zeros in mask? */
334 for (i
= 0; i
< 64; i
++)
335 if (! (badmask
& (1ULL << i
)))
338 if (fill_mask (badaddr
, badmask
, 0) >= addr
)
344 /* Find starting value. Keep low and high such that
345 fill_mask (low) < addr and fill_mask (high) >= addr;
347 while (high
- low
> 1)
349 cur
= (low
+ high
) / 2;
350 if (fill_mask (badaddr
, badmask
, cur
) >= addr
)
358 for (; iterator
< (1ULL << (var
- tail
))
359 && (cur
= fill_mask (badaddr
, badmask
, iterator
)) < addr
+ size
;
362 grub_dprintf ("badram", "%llx (size %llx) is a badram range\n",
363 (unsigned long long) cur
, (1ULL << tail
));
364 grub_mmap_register (cur
, (1ULL << tail
), GRUB_MEMORY_HOLE
);
370 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "badram string required");
372 grub_dprintf ("badram", "executing badram\n");
378 /* Parse address and mask. */
379 badaddr
= grub_strtoull (str
, &str
, 16);
382 badmask
= grub_strtoull (str
, &str
, 16);
386 if (grub_errno
== GRUB_ERR_BAD_NUMBER
)
389 return GRUB_ERR_NONE
;
392 /* When part of a page is tainted, we discard the whole of it. There's
393 no point in providing sub-page chunks. */
394 badmask
&= ~(CHUNK_SIZE
- 1);
396 grub_dprintf ("badram", "badram %llx:%llx\n",
397 (unsigned long long) badaddr
, (unsigned long long) badmask
);
399 grub_mmap_iterate (hook
);
404 parsemem (const char *str
)
409 ret
= grub_strtoul (str
, &ptr
, 0);
426 grub_cmd_cutmem (grub_command_t cmd
__attribute__ ((unused
)),
427 int argc
, char **args
)
429 grub_uint64_t from
, to
;
431 auto int NESTED_FUNC_ATTR
hook (grub_uint64_t
, grub_uint64_t
,
433 int NESTED_FUNC_ATTR
hook (grub_uint64_t addr
,
435 grub_memory_type_t type
__attribute__ ((unused
)))
437 grub_uint64_t end
= addr
+ size
;
447 grub_mmap_register (addr
, end
- addr
, GRUB_MEMORY_HOLE
);
452 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "argements required");
454 from
= parsemem (args
[0]);
458 to
= parsemem (args
[1]);
462 grub_mmap_iterate (hook
);
464 return GRUB_ERR_NONE
;
467 static grub_command_t cmd
, cmd_cut
;
472 cmd
= grub_register_command ("badram", grub_cmd_badram
,
473 N_("ADDR1,MASK1[,ADDR2,MASK2[,...]]"),
474 N_("Declare memory regions as badram."));
475 cmd_cut
= grub_register_command ("cutmem", grub_cmd_cutmem
,
476 N_("FROM[K|M|G] TO[K|M|G]"),
477 N_("Remove any memory regions in specified range."));
483 grub_unregister_command (cmd
);
484 grub_unregister_command (cmd_cut
);