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 * Common code for "shuffle and boot" operation; generates a shuffle list
33 * and puts it in the bounce buffer. Returns the number of shuffle
42 #include <syslinux/movebits.h>
43 #include <klibc/compiler.h>
49 #define dprintf(f, ...) ((void)0)
50 #define dprintf2(f, ...) ((void)0)
55 # define dprintf printf
58 # define dprintf2 printf
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, ®
, ®
);
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
,
90 struct syslinux_movelist
*moves
= NULL
, *mp
;
91 struct syslinux_memmap
*rxmap
= NULL
, *ml
;
92 struct shuffle_descriptor
*dp
, *dbuf
;
94 int desc_blocks
, need_blocks
;
96 addr_t desczone
, descfree
, descaddr
, descoffs
;
103 /* Count the number of zero operations */
105 for (ml
= memmap
; ml
->type
!= SMT_END
; ml
= ml
->next
) {
106 if (ml
->type
== SMT_ZERO
)
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
);
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
))
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
))
125 if (syslinux_memmap_largest(rxmap
, SMT_FREE
, &desczone
, &descfree
))
128 syslinux_free_memmap(rxmap
);
130 dprintf("desczone = 0x%08x, descfree = 0x%08x\n", desczone
, descfree
);
132 rxmap
= syslinux_dup_memmap(memmap
);
136 desc_blocks
= (nzero
+ DESC_BLOCK_SIZE
- 1) / DESC_BLOCK_SIZE
;
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
))
154 syslinux_dump_movelist(stdout
, fraglist
);
157 if (syslinux_compute_movelist(&moves
, fraglist
, rxmap
))
161 for (mp
= moves
; mp
; mp
= mp
->next
)
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... */
173 dprintf("Final movelist:\n");
174 syslinux_dump_movelist(stdout
, moves
);
177 syslinux_free_memmap(rxmap
);
180 need_ptrs
= nmoves
+ nzero
+ 1;
181 dbuf
= malloc(need_ptrs
* sizeof(struct shuffle_descriptor
));
185 descoffs
= descaddr
- (addr_t
) dbuf
;
188 dprintf("nmoves = %d, nzero = %d, dbuf = %p, offs = 0x%08x\n",
189 nmoves
, nzero
, dbuf
, descoffs
);
192 /* Copy the move sequence into the descriptor buffer */
195 for (mp
= moves
; mp
; mp
= mp
->next
) {
199 dprintf2("[ %08x %08x %08x ]\n", dp
->dst
, dp
->src
, dp
->len
);
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
) {
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
);
216 /* Finally, record the termination entry */
217 dp
->dst
= entry_point
;
218 dp
->src
= entry_type
;
223 if (np
!= need_ptrs
) {
224 dprintf("!!! np = %d : nmoves = %d, nzero = %d, desc_blocks = %d\n",
225 np
, nmoves
, nzero
, desc_blocks
);
231 /* This is safe only because free() doesn't use the bounce buffer!!!! */
233 syslinux_free_movelist(moves
);
235 syslinux_free_memmap(rxmap
);
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
258 struct syslinux_memmap
*memmap
)
260 struct syslinux_memmap
*tmap
;
261 struct syslinux_movelist
*mp
;
263 tmap
= syslinux_dup_memmap(memmap
);
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
);