1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009 Intel Corporation; author: H. Peter Anvin
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following
15 * The above copyright notice and this permission notice shall
16 * be included in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
27 * ----------------------------------------------------------------------- */
32 * Deal with syslinux_memmap's, which are data structures designed to
33 * hold memory maps. A zonelist is a sorted linked list of memory
34 * ranges, with the guarantee that no two adjacent blocks have the
35 * same range type. Additionally, all unspecified memory have a range
40 #include <syslinux/align.h>
41 #include <syslinux/movebits.h>
45 * Create an empty syslinux_memmap list.
47 struct syslinux_memmap
*syslinux_init_memmap(void)
49 struct syslinux_memmap
*sp
, *ep
;
51 sp
= malloc(sizeof(*sp
));
55 ep
= malloc(sizeof(*ep
));
62 sp
->type
= SMT_UNDEFINED
;
65 ep
->start
= 0; /* Wrap around... */
66 ep
->type
= SMT_END
; /* End of chain */
73 * Add an item to a syslinux_memmap list, potentially overwriting
74 * what is already there.
76 int syslinux_add_memmap(struct syslinux_memmap
**list
,
77 addr_t start
, addr_t len
,
78 enum syslinux_memmap_types type
)
81 struct syslinux_memmap
*mp
, **mpp
;
82 struct syslinux_memmap
*range
;
83 enum syslinux_memmap_types oldtype
;
85 dprintf("Input memmap:\n");
86 syslinux_dump_memmap(*list
);
88 /* Remove this to make len == 0 mean all of memory */
92 /* Last byte -- to avoid rollover */
93 last
= start
+ len
- 1;
96 oldtype
= SMT_END
; /* Impossible value */
97 while (mp
= *mpp
, start
> mp
->start
&& mp
->type
!= SMT_END
) {
102 if (start
< mp
->start
|| mp
->type
== SMT_END
) {
103 if (type
!= oldtype
) {
104 /* Splice in a new start token */
105 range
= malloc(sizeof(*range
));
109 range
->start
= start
;
116 /* mp is exactly aligned with the start of our region */
117 if (type
!= oldtype
) {
118 /* Reclaim this entry as our own boundary marker */
125 while (mp
= *mpp
, last
> mp
->start
- 1) {
131 if (last
< mp
->start
- 1) {
132 if (oldtype
!= type
) {
133 /* Need a new end token */
134 range
= malloc(sizeof(*range
));
138 range
->start
= last
+ 1;
139 range
->type
= oldtype
;
144 if (mp
->type
== type
) {
145 /* Merge this region with the following one */
151 dprintf("After adding (%#x,%#x,%d):\n", start
, len
, type
);
152 syslinux_dump_memmap(*list
);
158 * Verify what type a certain memory region is. This function returns
159 * SMT_ERROR if the memory region has multiple types, except that
160 * SMT_FREE can be demoted to SMT_TERMINAL.
162 enum syslinux_memmap_types
syslinux_memmap_type(struct syslinux_memmap
*list
,
163 addr_t start
, addr_t len
)
167 last
= start
+ len
- 1;
169 while (list
->type
!= SMT_END
) {
170 llast
= list
->next
->start
- 1;
171 if (list
->start
<= start
) {
173 return list
->type
; /* Region has a well-defined type */
174 } else if (llast
>= start
) {
175 /* Crosses region boundary */
176 while (valid_terminal_type(list
->type
)) {
178 llast
= list
->next
->start
- 1;
188 return SMT_ERROR
; /* Internal error? */
192 * Find the largest zone of a specific type. Returns -1 on failure.
194 int syslinux_memmap_largest(struct syslinux_memmap
*list
,
195 enum syslinux_memmap_types type
,
196 addr_t
* start
, addr_t
* len
)
198 addr_t size
, best_size
= 0;
199 struct syslinux_memmap
*best
= NULL
;
201 while (list
->type
!= SMT_END
) {
202 size
= list
->next
->start
- list
->start
;
204 if (list
->type
== type
&& size
> best_size
) {
215 *start
= best
->start
;
222 * Find the highest zone of a specific type that satisfies the
225 * 'start' is updated with the highest address on success. 'start' can
226 * be used to set a minimum address to begin searching from.
228 * Returns -1 on failure.
230 int syslinux_memmap_highest(struct syslinux_memmap
*list
,
231 enum syslinux_memmap_types type
,
232 addr_t
*start
, addr_t len
,
233 addr_t ceiling
, addr_t align
)
237 for (best
= 0; list
->type
!= SMT_END
; list
= list
->next
) {
238 size
= list
->next
->start
- list
->start
;
240 if (list
->type
!= type
)
243 if (list
->start
+ size
<= *start
)
246 if (list
->start
+ len
>= ceiling
)
249 if (list
->start
+ size
< ceiling
)
250 best
= ALIGN_DOWN(list
->start
+ size
- len
, align
);
252 best
= ALIGN_DOWN(ceiling
- len
, align
);
267 * Find the first (lowest address) zone of a specific type and of
268 * a certain minimum size, with an optional starting address.
269 * The input values of start and len are used as minima.
271 int syslinux_memmap_find_type(struct syslinux_memmap
*list
,
272 enum syslinux_memmap_types type
,
273 addr_t
* start
, addr_t
* len
, addr_t align
)
275 addr_t min_start
= *start
;
276 addr_t min_len
= *len
;
278 while (list
->type
!= SMT_END
) {
279 if (list
->type
== type
) {
281 xstart
= min_start
> list
->start
? min_start
: list
->start
;
282 xstart
= ALIGN_UP(xstart
, align
);
284 if (xstart
< list
->next
->start
) {
285 xlen
= list
->next
->start
- xstart
;
286 if (xlen
>= min_len
) {
296 return -1; /* Not found */
302 void syslinux_free_memmap(struct syslinux_memmap
*list
)
304 struct syslinux_memmap
*ml
;
314 * Duplicate a zonelist. Returns NULL on failure.
316 struct syslinux_memmap
*syslinux_dup_memmap(struct syslinux_memmap
*list
)
318 struct syslinux_memmap
*newlist
= NULL
, **nlp
= &newlist
;
319 struct syslinux_memmap
*ml
;
322 ml
= malloc(sizeof(*ml
));
324 syslinux_free_memmap(newlist
);
327 ml
->start
= list
->start
;
328 ml
->type
= list
->type
;
340 * Find a memory region, given a set of heuristics and update 'base' if
343 int syslinux_memmap_find(struct syslinux_memmap
*mmap
,
344 addr_t
*base
, size_t size
,
345 bool relocate
, size_t align
,
346 addr_t start_min
, addr_t start_max
,
347 addr_t end_min
, addr_t end_max
)
349 const struct syslinux_memmap
*mp
;
350 enum syslinux_memmap_types type
;
356 type
= syslinux_memmap_type(mmap
, *base
, size
);
358 /* This assumes SMT_TERMINAL is OK if we can get the exact address */
359 if (valid_terminal_type(type
))
363 dprintf("Cannot relocate\n");
368 for (mp
= mmap
; mp
&& mp
->type
!= SMT_END
; mp
= mp
->next
) {
371 end
= mp
->next
->start
;
373 if (mp
->type
!= SMT_FREE
)
378 continue; /* Only relocate upwards */
380 if (start
< start_min
)
387 start
= ALIGN_UP(start
, align
);
388 if (start
> start_max
|| start
>= end
)
391 if (end
- start
>= size
) {