pxe: Fix recognition of keeppxe option
[syslinux.git] / com32 / lib / syslinux / zonelist.c
blobdbc874c5f7ccdafea86dedcaa9140d9ccd7cebac
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
13 * conditions:
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 * ----------------------------------------------------------------------- */
30 * zonelist.c
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
36 * type of zero.
39 #include <stdlib.h>
40 #include <syslinux/align.h>
41 #include <syslinux/movebits.h>
42 #include <dprintf.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));
52 if (!sp)
53 return NULL;
55 ep = malloc(sizeof(*ep));
56 if (!ep) {
57 free(sp);
58 return NULL;
61 sp->start = 0;
62 sp->type = SMT_UNDEFINED;
63 sp->next = ep;
65 ep->start = 0; /* Wrap around... */
66 ep->type = SMT_END; /* End of chain */
67 ep->next = NULL;
69 return sp;
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)
80 addr_t last;
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 */
89 if (len == 0)
90 return 0;
92 /* Last byte -- to avoid rollover */
93 last = start + len - 1;
95 mpp = list;
96 oldtype = SMT_END; /* Impossible value */
97 while (mp = *mpp, start > mp->start && mp->type != SMT_END) {
98 oldtype = mp->type;
99 mpp = &mp->next;
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));
106 if (!range)
107 return -1;
109 range->start = start;
110 range->type = type;
111 *mpp = range;
112 range->next = mp;
113 mpp = &range->next;
115 } else {
116 /* mp is exactly aligned with the start of our region */
117 if (type != oldtype) {
118 /* Reclaim this entry as our own boundary marker */
119 oldtype = mp->type;
120 mp->type = type;
121 mpp = &mp->next;
125 while (mp = *mpp, last > mp->start - 1) {
126 oldtype = mp->type;
127 *mpp = mp->next;
128 free(mp);
131 if (last < mp->start - 1) {
132 if (oldtype != type) {
133 /* Need a new end token */
134 range = malloc(sizeof(*range));
135 if (!range)
136 return -1;
138 range->start = last + 1;
139 range->type = oldtype;
140 *mpp = range;
141 range->next = mp;
143 } else {
144 if (mp->type == type) {
145 /* Merge this region with the following one */
146 *mpp = mp->next;
147 free(mp);
151 dprintf("After adding (%#x,%#x,%d):\n", start, len, type);
152 syslinux_dump_memmap(*list);
154 return 0;
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)
165 addr_t last, llast;
167 last = start + len - 1;
169 while (list->type != SMT_END) {
170 llast = list->next->start - 1;
171 if (list->start <= start) {
172 if (llast >= last) {
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)) {
177 list = list->next;
178 llast = list->next->start - 1;
179 if (llast >= last)
180 return SMT_TERMINAL;
182 return SMT_ERROR;
185 list = list->next;
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) {
205 best = list;
206 best_size = size;
209 list = list->next;
212 if (!best)
213 return -1;
215 *start = best->start;
216 *len = best_size;
218 return 0;
222 * Find the highest zone of a specific type that satisfies the
223 * constraints.
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(const struct syslinux_memmap *list,
231 enum syslinux_memmap_types type,
232 addr_t *start, addr_t len,
233 addr_t ceiling, addr_t align)
235 addr_t size, best;
237 for (best = 0; list->type != SMT_END; list = list->next) {
238 size = list->next->start - list->start;
240 if (list->type != type)
241 continue;
243 if (list->start + size <= *start)
244 continue;
246 if (list->start + len >= ceiling)
247 continue;
249 if (list->start + size < ceiling)
250 best = ALIGN_DOWN(list->start + size - len, align);
251 else
252 best = ALIGN_DOWN(ceiling - len, align);
254 if (best < *start)
255 best = 0;
258 if (!best)
259 return -1;
261 *start = best;
263 return 0;
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) {
280 addr_t xstart, xlen;
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) {
287 *start = xstart;
288 *len = xlen;
289 return 0;
293 list = list->next;
296 return -1; /* Not found */
300 * Free a zonelist.
302 void syslinux_free_memmap(struct syslinux_memmap *list)
304 struct syslinux_memmap *ml;
306 while (list) {
307 ml = list;
308 list = list->next;
309 free(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;
321 while (list) {
322 ml = malloc(sizeof(*ml));
323 if (!ml) {
324 syslinux_free_memmap(newlist);
325 return NULL;
327 ml->start = list->start;
328 ml->type = list->type;
329 ml->next = NULL;
330 *nlp = ml;
331 nlp = &ml->next;
333 list = list->next;
336 return newlist;
340 * Find a memory region, given a set of heuristics and update 'base' if
341 * successful.
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;
351 bool ok;
353 if (!size)
354 return 0;
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))
360 return 0;
362 if (!relocate) {
363 dprintf("Cannot relocate\n");
364 return -1;
367 ok = false;
368 for (mp = mmap; mp && mp->type != SMT_END; mp = mp->next) {
369 addr_t start, end;
370 start = mp->start;
371 end = mp->next->start;
373 if (mp->type != SMT_FREE)
374 continue;
376 /* min */
377 if (end <= end_min)
378 continue; /* Only relocate upwards */
380 if (start < start_min)
381 start = start_min;
383 /* max */
384 if (end > end_max)
385 end = end_max;
387 start = ALIGN_UP(start, align);
388 if (start > start_max || start >= end)
389 continue;
391 if (end - start >= size) {
392 *base = start;
393 ok = true;
394 break;
398 if (!ok)
399 return -1;
401 return 0;