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
44 #include <syslinux/movebits.h>
45 #include <klibc/compiler.h>
46 #include <syslinux/boot.h>
48 struct shuffle_descriptor
{
49 uint32_t dst
, src
, len
;
52 static int shuffler_size
;
54 static void __syslinux_get_shuffer_size(void)
57 /* +15 padding is to guarantee alignment */
58 shuffler_size
= __bcopyxx_len
+ 15;
63 * Allocate descriptor memory in these chunks; if this is large we may
64 * waste memory, if it is small we may get slow convergence.
66 #define DESC_BLOCK_SIZE 256
68 int syslinux_do_shuffle(struct syslinux_movelist
*fraglist
,
69 struct syslinux_memmap
*memmap
,
70 addr_t entry_point
, addr_t entry_type
,
74 struct syslinux_movelist
*moves
= NULL
, *mp
;
75 struct syslinux_memmap
*rxmap
= NULL
, *ml
;
76 struct shuffle_descriptor
*dp
, *dbuf
;
78 int desc_blocks
, need_blocks
;
80 addr_t desczone
, descfree
, descaddr
;
86 /* Count the number of zero operations */
88 for (ml
= memmap
; ml
->type
!= SMT_END
; ml
= ml
->next
) {
89 if (ml
->type
== SMT_ZERO
)
93 /* Find the largest contiguous region unused by input *and* output;
94 this is where we put the move descriptor list and safe area */
96 rxmap
= syslinux_dup_memmap(memmap
);
99 /* Avoid using the low 1 MB for the shuffle area -- this avoids
100 possible interference with the real mode code or stack */
101 if (syslinux_add_memmap(&rxmap
, 0, 1024 * 1024, SMT_RESERVED
))
103 for (mp
= fraglist
; mp
; mp
= mp
->next
) {
104 if (syslinux_add_memmap(&rxmap
, mp
->src
, mp
->len
, SMT_ALLOC
) ||
105 syslinux_add_memmap(&rxmap
, mp
->dst
, mp
->len
, SMT_ALLOC
))
108 if (syslinux_memmap_largest(rxmap
, SMT_FREE
, &desczone
, &descfree
))
111 syslinux_free_memmap(rxmap
);
113 dprintf("desczone = 0x%08x, descfree = 0x%08x\n", desczone
, descfree
);
115 rxmap
= syslinux_dup_memmap(memmap
);
119 __syslinux_get_shuffer_size();
120 desc_blocks
= (nzero
+ DESC_BLOCK_SIZE
- 1) / DESC_BLOCK_SIZE
;
122 /* We want (desc_blocks) allocation blocks, plus the terminating
123 descriptor, plus the shuffler safe area. */
124 addr_t descmem
= desc_blocks
*
125 sizeof(struct shuffle_descriptor
) * DESC_BLOCK_SIZE
126 + sizeof(struct shuffle_descriptor
) + shuffler_size
;
128 descaddr
= (desczone
+ descfree
- descmem
) & ~3;
130 if (descaddr
< desczone
)
131 goto bail
; /* No memory block large enough */
133 /* Mark memory used by shuffle descriptors as reserved */
134 if (syslinux_add_memmap(&rxmap
, descaddr
, descmem
, SMT_RESERVED
))
138 syslinux_dump_movelist(fraglist
);
141 if (syslinux_compute_movelist(&moves
, fraglist
, rxmap
))
145 for (mp
= moves
; mp
; mp
= mp
->next
)
148 need_blocks
= (nmoves
+ nzero
+ DESC_BLOCK_SIZE
- 1) / DESC_BLOCK_SIZE
;
150 if (desc_blocks
>= need_blocks
)
151 break; /* Sufficient memory, yay */
153 desc_blocks
= need_blocks
; /* Try again... */
157 dprintf("Final movelist:\n");
158 syslinux_dump_movelist(moves
);
161 syslinux_free_memmap(rxmap
);
164 need_ptrs
= nmoves
+ nzero
+ 1;
165 dbuf
= malloc(need_ptrs
* sizeof(struct shuffle_descriptor
));
171 addr_t descoffs
= descaddr
- (addr_t
) dbuf
;
173 dprintf("nmoves = %d, nzero = %d, dbuf = %p, offs = 0x%08x\n",
174 nmoves
, nzero
, dbuf
, descoffs
);
178 /* Copy the move sequence into the descriptor buffer */
181 for (mp
= moves
; mp
; mp
= mp
->next
) {
185 dprintf2("[ %08x %08x %08x ]\n", dp
->dst
, dp
->src
, dp
->len
);
190 /* Copy bzero operations into the descriptor buffer */
191 for (ml
= memmap
; ml
->type
!= SMT_END
; ml
= ml
->next
) {
192 if (ml
->type
== SMT_ZERO
) {
194 dp
->src
= (addr_t
) - 1; /* bzero region */
195 dp
->len
= ml
->next
->start
- ml
->start
;
196 dprintf2("[ %08x %08x %08x ]\n", dp
->dst
, dp
->src
, dp
->len
);
202 /* Finally, record the termination entry */
203 dp
->dst
= entry_point
;
204 dp
->src
= entry_type
;
209 if (np
!= need_ptrs
) {
210 dprintf("!!! np = %d : nmoves = %d, nzero = %d, desc_blocks = %d\n",
211 np
, nmoves
, nzero
, desc_blocks
);
217 /* This is safe only because free() doesn't use the bounce buffer!!!! */
219 syslinux_free_movelist(moves
);
221 syslinux_free_memmap(rxmap
);
226 /* Actually do it... */
227 bios_do_shuffle_and_boot(bootflags
, descaddr
, dbuf
,
228 (addr_t
)dp
- (addr_t
)dbuf
);
230 return -1; /* Shouldn't have returned! */
234 * Common helper routine: takes a memory map and blots out the
235 * zones which are used in the destination of a fraglist
237 struct syslinux_memmap
*syslinux_target_memmap(struct syslinux_movelist
239 struct syslinux_memmap
*memmap
)
241 struct syslinux_memmap
*tmap
;
242 struct syslinux_movelist
*mp
;
244 tmap
= syslinux_dup_memmap(memmap
);
248 for (mp
= fraglist
; mp
; mp
= mp
->next
) {
249 if (syslinux_add_memmap(&tmap
, mp
->dst
, mp
->len
, SMT_ALLOC
)) {
250 syslinux_free_memmap(tmap
);