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/machine/memory.h>
21 #include <grub/memory.h>
23 #include <grub/misc.h>
25 #include <grub/command.h>
28 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
30 struct grub_mmap_region
*grub_mmap_overlays
= 0;
31 static int curhandle
= 1;
36 grub_mmap_iterate (int NESTED_FUNC_ATTR (*hook
) (grub_uint64_t
,
37 grub_uint64_t
, grub_uint32_t
))
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
50 int priority
[GRUB_MACHINE_MEMORY_MAX_TYPE
+ 2] =
52 #ifdef GRUB_MACHINE_MEMORY_AVAILABLE
53 [GRUB_MACHINE_MEMORY_AVAILABLE
] = 1,
55 #ifdef GRUB_MACHINE_MEMORY_RESERVED
56 [GRUB_MACHINE_MEMORY_RESERVED
] = 3,
58 #ifdef GRUB_MACHINE_MEMORY_ACPI
59 [GRUB_MACHINE_MEMORY_ACPI
] = 2,
61 #ifdef GRUB_MACHINE_MEMORY_CODE
62 [GRUB_MACHINE_MEMORY_CODE
] = 3,
64 #ifdef GRUB_MACHINE_MEMORY_NVS
65 [GRUB_MACHINE_MEMORY_NVS
] = 3,
67 [GRUB_MACHINE_MEMORY_HOLE
] = 4,
72 /* Scanline events. */
75 /* At which memory address. */
77 /* 0 = region starts, 1 = region ends. */
79 /* Which type of memory region? */
82 struct grub_mmap_scan
*scanline_events
;
83 struct grub_mmap_scan t
;
85 /* Previous scanline event. */
86 grub_uint64_t lastaddr
;
88 /* Current scanline event. */
90 /* How many regions of given type overlap at current location? */
91 int present
[GRUB_MACHINE_MEMORY_MAX_TYPE
+ 2];
92 /* Number of mmap chunks. */
95 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
96 struct grub_mmap_region
*cur
;
99 auto int NESTED_FUNC_ATTR
count_hook (grub_uint64_t
, grub_uint64_t
,
101 int NESTED_FUNC_ATTR
count_hook (grub_uint64_t addr
__attribute__ ((unused
)),
102 grub_uint64_t size
__attribute__ ((unused
)),
103 grub_uint32_t type
__attribute__ ((unused
)))
109 auto int NESTED_FUNC_ATTR
fill_hook (grub_uint64_t
, grub_uint64_t
,
111 int NESTED_FUNC_ATTR
fill_hook (grub_uint64_t addr
,
115 scanline_events
[i
].pos
= addr
;
116 scanline_events
[i
].type
= 0;
117 if (type
<= GRUB_MACHINE_MEMORY_MAX_TYPE
&& priority
[type
])
118 scanline_events
[i
].memtype
= type
;
121 grub_dprintf ("mmap", "Unknown memory type %d. Assuming unusable\n",
123 scanline_events
[i
].memtype
= GRUB_MACHINE_MEMORY_RESERVED
;
127 scanline_events
[i
].pos
= addr
+ size
;
128 scanline_events
[i
].type
= 1;
129 scanline_events
[i
].memtype
= scanline_events
[i
- 1].memtype
;
137 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
138 for (cur
= grub_mmap_overlays
; cur
; cur
= cur
->next
)
142 grub_machine_mmap_iterate (count_hook
);
144 /* Initialize variables. */
145 grub_memset (present
, 0, sizeof (present
));
146 scanline_events
= (struct grub_mmap_scan
*)
147 grub_malloc (sizeof (struct grub_mmap_scan
) * 2 * mmap_num
);
149 if (! scanline_events
)
151 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
152 "couldn't allocate space for new memory map");
156 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
157 /* Register scanline events. */
158 for (cur
= grub_mmap_overlays
; cur
; cur
= cur
->next
)
160 scanline_events
[i
].pos
= cur
->start
;
161 scanline_events
[i
].type
= 0;
162 if (cur
->type
== GRUB_MACHINE_MEMORY_HOLE
163 || (cur
->type
>= 0 && cur
->type
<= GRUB_MACHINE_MEMORY_MAX_TYPE
164 && priority
[cur
->type
]))
165 scanline_events
[i
].memtype
= cur
->type
;
167 scanline_events
[i
].memtype
= GRUB_MACHINE_MEMORY_RESERVED
;
170 scanline_events
[i
].pos
= cur
->end
;
171 scanline_events
[i
].type
= 1;
172 scanline_events
[i
].memtype
= scanline_events
[i
- 1].memtype
;
175 #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
177 grub_machine_mmap_iterate (fill_hook
);
179 /* Primitive bubble sort. It has complexity O(n^2) but since we're
180 unlikely to have more than 100 chunks it's probably one of the
181 fastest for one purpose. */
186 for (i
= 0; i
< 2 * mmap_num
- 1; i
++)
187 if (scanline_events
[i
+ 1].pos
< scanline_events
[i
].pos
188 || (scanline_events
[i
+ 1].pos
== scanline_events
[i
].pos
189 && scanline_events
[i
+ 1].type
== 0
190 && scanline_events
[i
].type
== 1))
192 t
= scanline_events
[i
+ 1];
193 scanline_events
[i
+ 1] = scanline_events
[i
];
194 scanline_events
[i
] = t
;
199 lastaddr
= scanline_events
[0].pos
;
200 lasttype
= scanline_events
[0].memtype
;
201 for (i
= 0; i
< 2 * mmap_num
; i
++)
204 if (scanline_events
[i
].type
)
205 present
[scanline_events
[i
].memtype
]--;
207 present
[scanline_events
[i
].memtype
]++;
209 /* Determine current region type. */
211 for (k
= 0; k
<= GRUB_MACHINE_MEMORY_MAX_TYPE
+ 1; k
++)
212 if (present
[k
] && (curtype
== -1 || priority
[k
] > priority
[curtype
]))
215 /* Announce region to the hook if necessary. */
216 if ((curtype
== -1 || curtype
!= lasttype
)
217 && lastaddr
!= scanline_events
[i
].pos
219 && lasttype
!= GRUB_MACHINE_MEMORY_HOLE
220 && hook (lastaddr
, scanline_events
[i
].pos
- lastaddr
, lasttype
))
222 grub_free (scanline_events
);
223 return GRUB_ERR_NONE
;
226 /* Update last values if necessary. */
227 if (curtype
== -1 || curtype
!= lasttype
)
230 lastaddr
= scanline_events
[i
].pos
;
234 grub_free (scanline_events
);
235 return GRUB_ERR_NONE
;
238 #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
240 grub_mmap_register (grub_uint64_t start
, grub_uint64_t size
, int type
)
242 struct grub_mmap_region
*cur
;
244 grub_dprintf ("mmap", "registering\n");
246 cur
= (struct grub_mmap_region
*)
247 grub_malloc (sizeof (struct grub_mmap_region
));
250 grub_error (GRUB_ERR_OUT_OF_MEMORY
,
251 "couldn't allocate memory map overlay");
255 cur
->next
= grub_mmap_overlays
;
257 cur
->end
= start
+ size
;
259 cur
->handle
= curhandle
++;
260 grub_mmap_overlays
= cur
;
262 if (grub_machine_mmap_register (start
, size
, type
, curhandle
))
264 grub_mmap_overlays
= cur
->next
;
273 grub_mmap_unregister (int handle
)
275 struct grub_mmap_region
*cur
, *prev
;
277 for (cur
= grub_mmap_overlays
, prev
= 0; cur
; prev
= cur
, cur
= cur
->next
)
278 if (handle
== cur
->handle
)
281 if ((err
= grub_machine_mmap_unregister (handle
)))
285 prev
->next
= cur
->next
;
287 grub_mmap_overlays
= cur
->next
;
289 return GRUB_ERR_NONE
;
291 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "mmap overlay not found");
294 #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
296 #define CHUNK_SIZE 0x400
298 static inline grub_uint64_t
299 fill_mask (grub_uint64_t addr
, grub_uint64_t mask
, grub_uint64_t iterator
)
302 grub_uint64_t ret
= (addr
& mask
);
304 /* Find first fixed bit. */
305 for (i
= 0; i
< 64; i
++)
306 if ((mask
& (1ULL << i
)) != 0)
310 if ((mask
& (1ULL << i
)) == 0)
312 if ((iterator
& (1ULL << j
)) != 0)
320 grub_cmd_badram (grub_command_t cmd
__attribute__ ((unused
)),
321 int argc
, char **args
)
324 grub_uint64_t badaddr
, badmask
;
326 auto int NESTED_FUNC_ATTR
hook (grub_uint64_t
, grub_uint64_t
, grub_uint32_t
);
327 int NESTED_FUNC_ATTR
hook (grub_uint64_t addr
,
329 grub_uint32_t type
__attribute__ ((unused
)))
331 grub_uint64_t iterator
, low
, high
, cur
;
334 grub_dprintf ("badram", "hook %llx+%llx\n", (unsigned long long) addr
,
335 (unsigned long long) size
);
337 /* How many trailing zeros? */
338 for (tail
= 0; ! (badmask
& (1ULL << tail
)); tail
++);
340 /* How many zeros in mask? */
342 for (i
= 0; i
< 64; i
++)
343 if (! (badmask
& (1ULL << i
)))
346 if (fill_mask (badaddr
, badmask
, 0) >= addr
)
352 /* Find starting value. Keep low and high such that
353 fill_mask (low) < addr and fill_mask (high) >= addr;
355 while (high
- low
> 1)
357 cur
= (low
+ high
) / 2;
358 if (fill_mask (badaddr
, badmask
, cur
) >= addr
)
366 for (; iterator
< (1ULL << (var
- tail
))
367 && (cur
= fill_mask (badaddr
, badmask
, iterator
)) < addr
+ size
;
370 grub_dprintf ("badram", "%llx (size %llx) is a badram range\n",
371 (long long) cur
, (long long) (1ULL << tail
) - 1);
372 grub_mmap_register (cur
, (1ULL << tail
) - 1, GRUB_MACHINE_MEMORY_HOLE
);
378 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "badram string required");
380 grub_dprintf ("badram", "executing badram\n");
386 /* Parse address and mask. */
387 badaddr
= grub_strtoull (str
, &str
, 16);
390 badmask
= grub_strtoull (str
, &str
, 16);
394 if (grub_errno
== GRUB_ERR_BAD_NUMBER
)
397 return GRUB_ERR_NONE
;
400 /* When part of a page is tainted, we discard the whole of it. There's
401 no point in providing sub-page chunks. */
402 badmask
&= ~(CHUNK_SIZE
- 1);
404 grub_dprintf ("badram", "badram %llx:%llx\n",
405 (unsigned long long) badaddr
, (unsigned long long) badmask
);
407 grub_mmap_iterate (hook
);
411 static grub_command_t cmd
;
416 cmd
= grub_register_command ("badram", grub_cmd_badram
,
417 "badram ADDR1,MASK1[,ADDR2,MASK2[,...]]",
418 "declare memory regions as badram");
423 grub_unregister_command (cmd
);