Adding upstream version 4.00~pre54+dfsg.
[syslinux-debian/hramrach.git] / com32 / lib / syslinux / shuffle.c
blob6b5a601fac4b397ce173f12341fbcd8e3c26477e
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 * shuffle.c
32 * Common code for "shuffle and boot" operation; generates a shuffle list
33 * and puts it in the bounce buffer. Returns the number of shuffle
34 * descriptors.
37 #include <stdlib.h>
38 #include <string.h>
39 #include <inttypes.h>
40 #include <com32.h>
41 #include <minmax.h>
42 #include <syslinux/movebits.h>
43 #include <klibc/compiler.h>
45 #ifndef DEBUG
46 # define DEBUG 0
47 #endif
49 #define dprintf(f, ...) ((void)0)
50 #define dprintf2(f, ...) ((void)0)
52 #if DEBUG
53 # include <stdio.h>
54 # undef dprintf
55 # define dprintf printf
56 # if DEBUG > 1
57 # undef dprintf2
58 # define dprintf2 printf
59 # endif
60 #endif
62 struct shuffle_descriptor {
63 uint32_t dst, src, len;
66 static int shuffler_size;
68 static void __constructor __syslinux_get_shuffer_size(void)
70 static com32sys_t reg;
72 reg.eax.w[0] = 0x0023;
73 __intcall(0x22, &reg, &reg);
75 shuffler_size = (reg.eflags.l & EFLAGS_CF) ? 2048 : reg.ecx.w[0];
79 * Allocate descriptor memory in these chunks; if this is large we may
80 * waste memory, if it is small we may get slow convergence.
82 #define DESC_BLOCK_SIZE 256
84 int syslinux_do_shuffle(struct syslinux_movelist *fraglist,
85 struct syslinux_memmap *memmap,
86 addr_t entry_point, addr_t entry_type,
87 uint16_t bootflags)
89 int rv = -1;
90 struct syslinux_movelist *moves = NULL, *mp;
91 struct syslinux_memmap *rxmap = NULL, *ml;
92 struct shuffle_descriptor *dp, *dbuf;
93 int np;
94 int desc_blocks, need_blocks;
95 int need_ptrs;
96 addr_t desczone, descfree, descaddr, descoffs;
97 int nmoves, nzero;
98 com32sys_t ireg;
100 descaddr = 0;
101 dp = dbuf = NULL;
103 /* Count the number of zero operations */
104 nzero = 0;
105 for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
106 if (ml->type == SMT_ZERO)
107 nzero++;
110 /* Find the largest contiguous region unused by input *and* output;
111 this is where we put the move descriptor list and safe area */
113 rxmap = syslinux_dup_memmap(memmap);
114 if (!rxmap)
115 goto bail;
116 /* Avoid using the low 1 MB for the shuffle area -- this avoids
117 possible interference with the real mode code or stack */
118 if (syslinux_add_memmap(&rxmap, 0, 1024 * 1024, SMT_RESERVED))
119 goto bail;
120 for (mp = fraglist; mp; mp = mp->next) {
121 if (syslinux_add_memmap(&rxmap, mp->src, mp->len, SMT_ALLOC) ||
122 syslinux_add_memmap(&rxmap, mp->dst, mp->len, SMT_ALLOC))
123 goto bail;
125 if (syslinux_memmap_largest(rxmap, SMT_FREE, &desczone, &descfree))
126 goto bail;
128 syslinux_free_memmap(rxmap);
130 dprintf("desczone = 0x%08x, descfree = 0x%08x\n", desczone, descfree);
132 rxmap = syslinux_dup_memmap(memmap);
133 if (!rxmap)
134 goto bail;
136 desc_blocks = (nzero + DESC_BLOCK_SIZE - 1) / DESC_BLOCK_SIZE;
137 for (;;) {
138 /* We want (desc_blocks) allocation blocks, plus the terminating
139 descriptor, plus the shuffler safe area. */
140 addr_t descmem = desc_blocks *
141 sizeof(struct shuffle_descriptor) * DESC_BLOCK_SIZE
142 + sizeof(struct shuffle_descriptor) + shuffler_size;
144 descaddr = (desczone + descfree - descmem) & ~3;
146 if (descaddr < desczone)
147 goto bail; /* No memory block large enough */
149 /* Mark memory used by shuffle descriptors as reserved */
150 if (syslinux_add_memmap(&rxmap, descaddr, descmem, SMT_RESERVED))
151 goto bail;
153 #if DEBUG > 1
154 syslinux_dump_movelist(stdout, fraglist);
155 #endif
157 if (syslinux_compute_movelist(&moves, fraglist, rxmap))
158 goto bail;
160 nmoves = 0;
161 for (mp = moves; mp; mp = mp->next)
162 nmoves++;
164 need_blocks = (nmoves + nzero + DESC_BLOCK_SIZE - 1) / DESC_BLOCK_SIZE;
166 if (desc_blocks >= need_blocks)
167 break; /* Sufficient memory, yay */
169 desc_blocks = need_blocks; /* Try again... */
172 #if DEBUG > 1
173 dprintf("Final movelist:\n");
174 syslinux_dump_movelist(stdout, moves);
175 #endif
177 syslinux_free_memmap(rxmap);
178 rxmap = NULL;
180 need_ptrs = nmoves + nzero + 1;
181 dbuf = malloc(need_ptrs * sizeof(struct shuffle_descriptor));
182 if (!dbuf)
183 goto bail;
185 descoffs = descaddr - (addr_t) dbuf;
187 #if DEBUG
188 dprintf("nmoves = %d, nzero = %d, dbuf = %p, offs = 0x%08x\n",
189 nmoves, nzero, dbuf, descoffs);
190 #endif
192 /* Copy the move sequence into the descriptor buffer */
193 np = 0;
194 dp = dbuf;
195 for (mp = moves; mp; mp = mp->next) {
196 dp->dst = mp->dst;
197 dp->src = mp->src;
198 dp->len = mp->len;
199 dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len);
200 dp++;
201 np++;
204 /* Copy bzero operations into the descriptor buffer */
205 for (ml = memmap; ml->type != SMT_END; ml = ml->next) {
206 if (ml->type == SMT_ZERO) {
207 dp->dst = ml->start;
208 dp->src = (addr_t) - 1; /* bzero region */
209 dp->len = ml->next->start - ml->start;
210 dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len);
211 dp++;
212 np++;
216 /* Finally, record the termination entry */
217 dp->dst = entry_point;
218 dp->src = entry_type;
219 dp->len = 0;
220 dp++;
221 np++;
223 if (np != need_ptrs) {
224 dprintf("!!! np = %d : nmoves = %d, nzero = %d, desc_blocks = %d\n",
225 np, nmoves, nzero, desc_blocks);
228 rv = 0;
230 bail:
231 /* This is safe only because free() doesn't use the bounce buffer!!!! */
232 if (moves)
233 syslinux_free_movelist(moves);
234 if (rxmap)
235 syslinux_free_memmap(rxmap);
237 if (rv)
238 return rv;
240 /* Actually do it... */
241 memset(&ireg, 0, sizeof ireg);
242 ireg.edi.l = descaddr;
243 ireg.esi.l = (addr_t) dbuf;
244 ireg.ecx.l = (addr_t) dp - (addr_t) dbuf;
245 ireg.edx.w[0] = bootflags;
246 ireg.eax.w[0] = 0x0024;
247 __intcall(0x22, &ireg, NULL);
249 return -1; /* Shouldn't have returned! */
253 * Common helper routine: takes a memory map and blots out the
254 * zones which are used in the destination of a fraglist
256 struct syslinux_memmap *syslinux_target_memmap(struct syslinux_movelist
257 *fraglist,
258 struct syslinux_memmap *memmap)
260 struct syslinux_memmap *tmap;
261 struct syslinux_movelist *mp;
263 tmap = syslinux_dup_memmap(memmap);
264 if (!tmap)
265 return NULL;
267 for (mp = fraglist; mp; mp = mp->next) {
268 if (syslinux_add_memmap(&tmap, mp->dst, mp->len, SMT_ALLOC)) {
269 syslinux_free_memmap(tmap);
270 return NULL;
274 return tmap;