[e16e8f2] | 1 | /* ----------------------------------------------------------------------- * |
---|
| 2 | * |
---|
| 3 | * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved |
---|
| 4 | * Copyright 2009-2013 Intel Corporation; author: H. Peter Anvin |
---|
| 5 | * |
---|
| 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: |
---|
| 14 | * |
---|
| 15 | * The above copyright notice and this permission notice shall |
---|
| 16 | * be included in all copies or substantial portions of the Software. |
---|
| 17 | * |
---|
| 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. |
---|
| 26 | * |
---|
| 27 | * ----------------------------------------------------------------------- */ |
---|
| 28 | |
---|
| 29 | /* |
---|
| 30 | * load_linux.c |
---|
| 31 | * |
---|
| 32 | * Load a Linux kernel (Image/zImage/bzImage). |
---|
| 33 | */ |
---|
| 34 | |
---|
| 35 | #include <ctype.h> |
---|
| 36 | #include <stdbool.h> |
---|
| 37 | #include <stdlib.h> |
---|
| 38 | #include <inttypes.h> |
---|
| 39 | #include <string.h> |
---|
| 40 | #include <minmax.h> |
---|
| 41 | #include <errno.h> |
---|
| 42 | #include <suffix_number.h> |
---|
| 43 | #include <dprintf.h> |
---|
| 44 | |
---|
| 45 | #include <syslinux/align.h> |
---|
| 46 | #include <syslinux/linux.h> |
---|
| 47 | #include <syslinux/bootrm.h> |
---|
| 48 | #include <syslinux/movebits.h> |
---|
| 49 | #include <syslinux/firmware.h> |
---|
| 50 | #include <syslinux/video.h> |
---|
| 51 | |
---|
| 52 | #define BOOT_MAGIC 0xAA55 |
---|
| 53 | #define LINUX_MAGIC ('H' + ('d' << 8) + ('r' << 16) + ('S' << 24)) |
---|
| 54 | #define OLD_CMDLINE_MAGIC 0xA33F |
---|
| 55 | |
---|
| 56 | /* loadflags */ |
---|
| 57 | #define LOAD_HIGH 0x01 |
---|
| 58 | #define CAN_USE_HEAP 0x80 |
---|
| 59 | |
---|
| 60 | /* |
---|
| 61 | * Find the last instance of a particular command line argument |
---|
| 62 | * (which should include the final =; do not use for boolean arguments) |
---|
| 63 | * Note: the resulting string is typically not null-terminated. |
---|
| 64 | */ |
---|
| 65 | static const char *find_argument(const char *cmdline, const char *argument) |
---|
| 66 | { |
---|
| 67 | const char *found = NULL; |
---|
| 68 | const char *p = cmdline; |
---|
| 69 | bool was_space = true; |
---|
| 70 | size_t la = strlen(argument); |
---|
| 71 | |
---|
| 72 | while (*p) { |
---|
| 73 | if (isspace(*p)) { |
---|
| 74 | was_space = true; |
---|
| 75 | } else if (was_space) { |
---|
| 76 | if (!memcmp(p, argument, la)) |
---|
| 77 | found = p + la; |
---|
| 78 | was_space = false; |
---|
| 79 | } |
---|
| 80 | p++; |
---|
| 81 | } |
---|
| 82 | |
---|
| 83 | return found; |
---|
| 84 | } |
---|
| 85 | |
---|
| 86 | /* Truncate to 32 bits, with saturate */ |
---|
| 87 | static inline uint32_t saturate32(unsigned long long v) |
---|
| 88 | { |
---|
| 89 | return (v > 0xffffffff) ? 0xffffffff : (uint32_t) v; |
---|
| 90 | } |
---|
| 91 | |
---|
| 92 | /* Create the appropriate mappings for the initramfs */ |
---|
| 93 | static int map_initramfs(struct syslinux_movelist **fraglist, |
---|
| 94 | struct syslinux_memmap **mmap, |
---|
| 95 | struct initramfs *initramfs, addr_t addr) |
---|
| 96 | { |
---|
| 97 | struct initramfs *ip; |
---|
| 98 | addr_t next_addr, len, pad; |
---|
| 99 | |
---|
| 100 | for (ip = initramfs->next; ip->len; ip = ip->next) { |
---|
| 101 | len = ip->len; |
---|
| 102 | next_addr = addr + len; |
---|
| 103 | |
---|
| 104 | /* If this isn't the last entry, extend the zero-pad region |
---|
| 105 | to enforce the alignment of the next chunk. */ |
---|
| 106 | if (ip->next->len) { |
---|
| 107 | pad = -next_addr & (ip->next->align - 1); |
---|
| 108 | len += pad; |
---|
| 109 | next_addr += pad; |
---|
| 110 | } |
---|
| 111 | |
---|
| 112 | if (ip->data_len) { |
---|
| 113 | if (syslinux_add_movelist(fraglist, addr, (addr_t) ip->data, len)) |
---|
| 114 | return -1; |
---|
| 115 | } |
---|
| 116 | if (len > ip->data_len) { |
---|
| 117 | if (syslinux_add_memmap(mmap, addr + ip->data_len, |
---|
| 118 | len - ip->data_len, SMT_ZERO)) |
---|
| 119 | return -1; |
---|
| 120 | } |
---|
| 121 | addr = next_addr; |
---|
| 122 | } |
---|
| 123 | |
---|
| 124 | return 0; |
---|
| 125 | } |
---|
| 126 | |
---|
| 127 | static size_t calc_cmdline_offset(const struct syslinux_memmap *mmap, |
---|
| 128 | const struct linux_header *hdr, |
---|
| 129 | size_t cmdline_size, addr_t base, |
---|
| 130 | addr_t start) |
---|
| 131 | { |
---|
| 132 | size_t max_offset; |
---|
| 133 | |
---|
| 134 | if (hdr->version >= 0x0202 && (hdr->loadflags & LOAD_HIGH)) |
---|
| 135 | max_offset = 0x10000; |
---|
| 136 | else |
---|
| 137 | max_offset = 0xfff0 - cmdline_size; |
---|
| 138 | |
---|
| 139 | if (!syslinux_memmap_highest(mmap, SMT_FREE, &start, |
---|
| 140 | cmdline_size, 0xa0000, 16) || |
---|
| 141 | !syslinux_memmap_highest(mmap, SMT_TERMINAL, &start, |
---|
| 142 | cmdline_size, 0xa0000, 16)) { |
---|
| 143 | |
---|
| 144 | |
---|
| 145 | return min(start - base, max_offset) & ~15; |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | dprintf("Unable to find lowmem for cmdline\n"); |
---|
| 149 | return (0x9ff0 - cmdline_size) & ~15; /* Legacy value: pure hope... */ |
---|
| 150 | } |
---|
| 151 | |
---|
| 152 | int bios_boot_linux(void *kernel_buf, size_t kernel_size, |
---|
| 153 | struct initramfs *initramfs, |
---|
| 154 | struct setup_data *setup_data, |
---|
| 155 | char *cmdline) |
---|
| 156 | { |
---|
| 157 | struct linux_header hdr, *whdr; |
---|
| 158 | size_t real_mode_size, prot_mode_size, base; |
---|
| 159 | addr_t real_mode_base, prot_mode_base, prot_mode_max; |
---|
| 160 | addr_t irf_size; |
---|
| 161 | size_t cmdline_size, cmdline_offset; |
---|
| 162 | struct setup_data *sdp; |
---|
| 163 | struct syslinux_rm_regs regs; |
---|
| 164 | struct syslinux_movelist *fraglist = NULL; |
---|
| 165 | struct syslinux_memmap *mmap = NULL; |
---|
| 166 | struct syslinux_memmap *amap = NULL; |
---|
| 167 | uint32_t memlimit = 0; |
---|
| 168 | uint16_t video_mode = 0; |
---|
| 169 | const char *arg; |
---|
| 170 | |
---|
| 171 | cmdline_size = strlen(cmdline) + 1; |
---|
| 172 | |
---|
| 173 | errno = EINVAL; |
---|
| 174 | if (kernel_size < 2 * 512) { |
---|
| 175 | dprintf("Kernel size too small\n"); |
---|
| 176 | goto bail; |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | /* Look for specific command-line arguments we care about */ |
---|
| 180 | if ((arg = find_argument(cmdline, "mem="))) |
---|
| 181 | memlimit = saturate32(suffix_number(arg)); |
---|
| 182 | |
---|
| 183 | if ((arg = find_argument(cmdline, "vga="))) { |
---|
| 184 | switch (arg[0] | 0x20) { |
---|
| 185 | case 'a': /* "ask" */ |
---|
| 186 | video_mode = 0xfffd; |
---|
| 187 | break; |
---|
| 188 | case 'e': /* "ext" */ |
---|
| 189 | video_mode = 0xfffe; |
---|
| 190 | break; |
---|
| 191 | case 'n': /* "normal" */ |
---|
| 192 | video_mode = 0xffff; |
---|
| 193 | break; |
---|
| 194 | case 'c': /* "current" */ |
---|
| 195 | video_mode = 0x0f04; |
---|
| 196 | break; |
---|
| 197 | default: |
---|
| 198 | video_mode = strtoul(arg, NULL, 0); |
---|
| 199 | break; |
---|
| 200 | } |
---|
| 201 | } |
---|
| 202 | |
---|
| 203 | /* Copy the header into private storage */ |
---|
| 204 | /* Use whdr to modify the actual kernel header */ |
---|
| 205 | memcpy(&hdr, kernel_buf, sizeof hdr); |
---|
| 206 | whdr = (struct linux_header *)kernel_buf; |
---|
| 207 | |
---|
| 208 | if (hdr.boot_flag != BOOT_MAGIC) { |
---|
| 209 | dprintf("Invalid boot magic\n"); |
---|
| 210 | goto bail; |
---|
| 211 | } |
---|
| 212 | |
---|
| 213 | if (hdr.header != LINUX_MAGIC) { |
---|
| 214 | hdr.version = 0x0100; /* Very old kernel */ |
---|
| 215 | hdr.loadflags = 0; |
---|
| 216 | } |
---|
| 217 | |
---|
| 218 | whdr->vid_mode = video_mode; |
---|
| 219 | |
---|
| 220 | if (!hdr.setup_sects) |
---|
| 221 | hdr.setup_sects = 4; |
---|
| 222 | |
---|
| 223 | if (hdr.version < 0x0203 || !hdr.initrd_addr_max) |
---|
| 224 | hdr.initrd_addr_max = 0x37ffffff; |
---|
| 225 | |
---|
| 226 | if (!memlimit && memlimit - 1 > hdr.initrd_addr_max) |
---|
| 227 | memlimit = hdr.initrd_addr_max + 1; /* Zero for no limit */ |
---|
| 228 | |
---|
| 229 | if (hdr.version < 0x0205 || !(hdr.loadflags & LOAD_HIGH)) |
---|
| 230 | hdr.relocatable_kernel = 0; |
---|
| 231 | |
---|
| 232 | if (hdr.version < 0x0206) |
---|
| 233 | hdr.cmdline_max_len = 256; |
---|
| 234 | |
---|
| 235 | if (cmdline_size > hdr.cmdline_max_len) { |
---|
| 236 | cmdline_size = hdr.cmdline_max_len; |
---|
| 237 | cmdline[cmdline_size - 1] = '\0'; |
---|
| 238 | } |
---|
| 239 | |
---|
| 240 | real_mode_size = (hdr.setup_sects + 1) << 9; |
---|
| 241 | real_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x10000 : 0x90000; |
---|
| 242 | prot_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x100000 : 0x10000; |
---|
| 243 | prot_mode_max = (hdr.loadflags & LOAD_HIGH) ? (addr_t)-1 : 0x8ffff; |
---|
| 244 | prot_mode_size = kernel_size - real_mode_size; |
---|
| 245 | |
---|
| 246 | /* Get the memory map */ |
---|
| 247 | mmap = syslinux_memory_map(); /* Memory map for shuffle_boot */ |
---|
| 248 | amap = syslinux_dup_memmap(mmap); /* Keep track of available memory */ |
---|
| 249 | if (!mmap || !amap) { |
---|
| 250 | errno = ENOMEM; |
---|
| 251 | goto bail; |
---|
| 252 | } |
---|
| 253 | |
---|
| 254 | cmdline_offset = calc_cmdline_offset(mmap, &hdr, cmdline_size, |
---|
| 255 | real_mode_base, |
---|
| 256 | real_mode_base + real_mode_size); |
---|
| 257 | dprintf("cmdline_offset at 0x%x\n", real_mode_base + cmdline_offset); |
---|
| 258 | |
---|
| 259 | if (hdr.version < 0x020a) { |
---|
| 260 | /* |
---|
| 261 | * The 3* here is a total fudge factor... it's supposed to |
---|
| 262 | * account for the fact that the kernel needs to be |
---|
| 263 | * decompressed, and then followed by the BSS and BRK regions. |
---|
| 264 | * This doesn't, however, account for the fact that the kernel |
---|
| 265 | * is decompressed into a whole other place, either. |
---|
| 266 | */ |
---|
| 267 | hdr.init_size = 3 * prot_mode_size; |
---|
| 268 | } |
---|
| 269 | |
---|
| 270 | if (!(hdr.loadflags & LOAD_HIGH) && prot_mode_size > 512 * 1024) { |
---|
| 271 | dprintf("Kernel cannot be loaded low\n"); |
---|
| 272 | goto bail; |
---|
| 273 | } |
---|
| 274 | |
---|
| 275 | /* Get the size of the initramfs, if there is one */ |
---|
| 276 | irf_size = initramfs_size(initramfs); |
---|
| 277 | |
---|
| 278 | if (irf_size && hdr.version < 0x0200) { |
---|
| 279 | dprintf("Initrd specified but not supported by kernel\n"); |
---|
| 280 | goto bail; |
---|
| 281 | } |
---|
| 282 | |
---|
| 283 | if (hdr.version >= 0x0200) { |
---|
| 284 | whdr->type_of_loader = 0x30; /* SYSLINUX unknown module */ |
---|
| 285 | if (hdr.version >= 0x0201) { |
---|
| 286 | whdr->heap_end_ptr = cmdline_offset - 0x0200; |
---|
| 287 | whdr->loadflags |= CAN_USE_HEAP; |
---|
| 288 | } |
---|
| 289 | } |
---|
| 290 | |
---|
| 291 | dprintf("Initial memory map:\n"); |
---|
| 292 | syslinux_dump_memmap(mmap); |
---|
| 293 | |
---|
| 294 | /* If the user has specified a memory limit, mark that as unavailable. |
---|
| 295 | Question: should we mark this off-limit in the mmap as well (meaning |
---|
| 296 | it's unavailable to the boot loader, which probably has already touched |
---|
| 297 | some of it), or just in the amap? */ |
---|
| 298 | if (memlimit) |
---|
| 299 | if (syslinux_add_memmap(&amap, memlimit, -memlimit, SMT_RESERVED)) { |
---|
| 300 | errno = ENOMEM; |
---|
| 301 | goto bail; |
---|
| 302 | } |
---|
| 303 | |
---|
| 304 | /* Place the kernel in memory */ |
---|
| 305 | |
---|
| 306 | /* |
---|
| 307 | * First, find a suitable place for the protected-mode code. If |
---|
| 308 | * the kernel image is not relocatable, just worry if it fits (it |
---|
| 309 | * might not even be a Linux image, after all, and for !LOAD_HIGH |
---|
| 310 | * we end up decompressing into a different location anyway), but |
---|
| 311 | * if it is, make sure everything fits. |
---|
| 312 | */ |
---|
| 313 | base = prot_mode_base; |
---|
| 314 | if (prot_mode_size && |
---|
| 315 | syslinux_memmap_find(amap, &base, |
---|
| 316 | hdr.relocatable_kernel ? |
---|
| 317 | hdr.init_size : prot_mode_size, |
---|
| 318 | hdr.relocatable_kernel, hdr.kernel_alignment, |
---|
| 319 | prot_mode_base, prot_mode_max, |
---|
| 320 | prot_mode_base, prot_mode_max)) { |
---|
| 321 | dprintf("Could not find location for protected-mode code\n"); |
---|
| 322 | goto bail; |
---|
| 323 | } |
---|
| 324 | |
---|
| 325 | whdr->code32_start += base - prot_mode_base; |
---|
| 326 | |
---|
| 327 | /* Real mode code */ |
---|
| 328 | if (syslinux_memmap_find(amap, &real_mode_base, |
---|
| 329 | cmdline_offset + cmdline_size, true, 16, |
---|
| 330 | real_mode_base, 0x90000, 0, 640*1024)) { |
---|
| 331 | dprintf("Could not find location for real-mode code\n"); |
---|
| 332 | goto bail; |
---|
| 333 | } |
---|
| 334 | |
---|
| 335 | if (syslinux_add_movelist(&fraglist, real_mode_base, (addr_t) kernel_buf, |
---|
| 336 | real_mode_size)) |
---|
| 337 | goto bail; |
---|
| 338 | if (syslinux_add_memmap |
---|
| 339 | (&amap, real_mode_base, cmdline_offset + cmdline_size, SMT_ALLOC)) { |
---|
| 340 | errno = ENOMEM; |
---|
| 341 | goto bail; |
---|
| 342 | } |
---|
| 343 | |
---|
| 344 | /* Zero region between real mode code and cmdline */ |
---|
| 345 | if (syslinux_add_memmap(&mmap, real_mode_base + real_mode_size, |
---|
| 346 | cmdline_offset - real_mode_size, SMT_ZERO)) { |
---|
| 347 | errno = ENOMEM; |
---|
| 348 | goto bail; |
---|
| 349 | } |
---|
| 350 | |
---|
| 351 | /* Command line */ |
---|
| 352 | if (syslinux_add_movelist(&fraglist, real_mode_base + cmdline_offset, |
---|
| 353 | (addr_t) cmdline, cmdline_size)) { |
---|
| 354 | errno = ENOMEM; |
---|
| 355 | goto bail; |
---|
| 356 | } |
---|
| 357 | if (hdr.version >= 0x0202) { |
---|
| 358 | whdr->cmd_line_ptr = real_mode_base + cmdline_offset; |
---|
| 359 | } else { |
---|
| 360 | whdr->old_cmd_line_magic = OLD_CMDLINE_MAGIC; |
---|
| 361 | whdr->old_cmd_line_offset = cmdline_offset; |
---|
| 362 | if (hdr.version >= 0x0200) { |
---|
| 363 | /* Be paranoid and round up to a multiple of 16 */ |
---|
| 364 | whdr->setup_move_size = (cmdline_offset + cmdline_size + 15) & ~15; |
---|
| 365 | } |
---|
| 366 | } |
---|
| 367 | |
---|
| 368 | /* Protected-mode code */ |
---|
| 369 | if (prot_mode_size) { |
---|
| 370 | if (syslinux_add_movelist(&fraglist, prot_mode_base, |
---|
| 371 | (addr_t) kernel_buf + real_mode_size, |
---|
| 372 | prot_mode_size)) { |
---|
| 373 | errno = ENOMEM; |
---|
| 374 | goto bail; |
---|
| 375 | } |
---|
| 376 | if (syslinux_add_memmap(&amap, prot_mode_base, prot_mode_size, |
---|
| 377 | SMT_ALLOC)) { |
---|
| 378 | errno = ENOMEM; |
---|
| 379 | goto bail; |
---|
| 380 | } |
---|
| 381 | } |
---|
| 382 | |
---|
| 383 | /* Figure out the size of the initramfs, and where to put it. |
---|
| 384 | We should put it at the highest possible address which is |
---|
| 385 | <= hdr.initrd_addr_max, which fits the entire initramfs. */ |
---|
| 386 | |
---|
| 387 | if (irf_size) { |
---|
| 388 | addr_t best_addr = 0; |
---|
| 389 | struct syslinux_memmap *ml; |
---|
| 390 | const addr_t align_mask = INITRAMFS_MAX_ALIGN - 1; |
---|
| 391 | |
---|
| 392 | if (irf_size) { |
---|
| 393 | for (ml = amap; ml->type != SMT_END; ml = ml->next) { |
---|
| 394 | addr_t adj_start = (ml->start + align_mask) & ~align_mask; |
---|
| 395 | addr_t adj_end = ml->next->start & ~align_mask; |
---|
| 396 | if (ml->type == SMT_FREE && adj_end - adj_start >= irf_size) |
---|
| 397 | best_addr = (adj_end - irf_size) & ~align_mask; |
---|
| 398 | } |
---|
| 399 | |
---|
| 400 | if (!best_addr) { |
---|
| 401 | dprintf("Insufficient memory for initramfs\n"); |
---|
| 402 | goto bail; |
---|
| 403 | } |
---|
| 404 | |
---|
| 405 | whdr->ramdisk_image = best_addr; |
---|
| 406 | whdr->ramdisk_size = irf_size; |
---|
| 407 | |
---|
| 408 | if (syslinux_add_memmap(&amap, best_addr, irf_size, SMT_ALLOC)) { |
---|
| 409 | errno = ENOMEM; |
---|
| 410 | goto bail; |
---|
| 411 | } |
---|
| 412 | |
---|
| 413 | if (map_initramfs(&fraglist, &mmap, initramfs, best_addr)) { |
---|
| 414 | errno = ENOMEM; |
---|
| 415 | goto bail; |
---|
| 416 | } |
---|
| 417 | } |
---|
| 418 | } |
---|
| 419 | |
---|
| 420 | if (setup_data) { |
---|
| 421 | uint64_t *prev_ptr = &whdr->setup_data; |
---|
| 422 | |
---|
| 423 | for (sdp = setup_data->next; sdp != setup_data; sdp = sdp->next) { |
---|
| 424 | struct syslinux_memmap *ml; |
---|
| 425 | const addr_t align_mask = 15; /* Header is 16 bytes */ |
---|
| 426 | addr_t best_addr = 0; |
---|
| 427 | size_t size = sdp->hdr.len + sizeof(sdp->hdr); |
---|
| 428 | |
---|
| 429 | if (!sdp->data || !sdp->hdr.len) |
---|
| 430 | continue; |
---|
| 431 | |
---|
| 432 | if (hdr.version < 0x0209) { |
---|
| 433 | /* Setup data not supported */ |
---|
| 434 | errno = ENXIO; /* Kind of arbitrary... */ |
---|
| 435 | goto bail; |
---|
| 436 | } |
---|
| 437 | |
---|
| 438 | for (ml = amap; ml->type != SMT_END; ml = ml->next) { |
---|
| 439 | addr_t adj_start = (ml->start + align_mask) & ~align_mask; |
---|
| 440 | addr_t adj_end = ml->next->start & ~align_mask; |
---|
| 441 | |
---|
| 442 | if (ml->type == SMT_FREE && adj_end - adj_start >= size) |
---|
| 443 | best_addr = (adj_end - size) & ~align_mask; |
---|
| 444 | } |
---|
| 445 | |
---|
| 446 | if (!best_addr) |
---|
| 447 | goto bail; |
---|
| 448 | |
---|
| 449 | *prev_ptr = best_addr; |
---|
| 450 | prev_ptr = &sdp->hdr.next; |
---|
| 451 | |
---|
| 452 | if (syslinux_add_memmap(&amap, best_addr, size, SMT_ALLOC)) { |
---|
| 453 | errno = ENOMEM; |
---|
| 454 | goto bail; |
---|
| 455 | } |
---|
| 456 | if (syslinux_add_movelist(&fraglist, best_addr, |
---|
| 457 | (addr_t)&sdp->hdr, sizeof sdp->hdr)) { |
---|
| 458 | errno = ENOMEM; |
---|
| 459 | goto bail; |
---|
| 460 | } |
---|
| 461 | if (syslinux_add_movelist(&fraglist, best_addr + sizeof sdp->hdr, |
---|
| 462 | (addr_t)sdp->data, sdp->hdr.len)) { |
---|
| 463 | errno = ENOMEM; |
---|
| 464 | goto bail; |
---|
| 465 | } |
---|
| 466 | } |
---|
| 467 | } |
---|
| 468 | |
---|
| 469 | /* Set up the registers on entry */ |
---|
| 470 | memset(®s, 0, sizeof regs); |
---|
| 471 | regs.es = regs.ds = regs.ss = regs.fs = regs.gs = real_mode_base >> 4; |
---|
| 472 | regs.cs = (real_mode_base >> 4) + 0x20; |
---|
| 473 | /* regs.ip = 0; */ |
---|
| 474 | /* Linux is OK with sp = 0 = 64K, but perhaps other things aren't... */ |
---|
| 475 | regs.esp.w[0] = min(cmdline_offset, (size_t) 0xfff0); |
---|
| 476 | |
---|
| 477 | dprintf("Final memory map:\n"); |
---|
| 478 | syslinux_dump_memmap(mmap); |
---|
| 479 | |
---|
| 480 | dprintf("Final available map:\n"); |
---|
| 481 | syslinux_dump_memmap(amap); |
---|
| 482 | |
---|
| 483 | dprintf("Initial movelist:\n"); |
---|
| 484 | syslinux_dump_movelist(fraglist); |
---|
| 485 | |
---|
| 486 | if (video_mode != 0x0f04) { |
---|
| 487 | /* |
---|
| 488 | * video_mode is not "current", so if we are in graphics mode we |
---|
| 489 | * need to revert to text mode... |
---|
| 490 | */ |
---|
| 491 | dprintf("*** Calling syslinux_force_text_mode()...\n"); |
---|
| 492 | syslinux_force_text_mode(); |
---|
| 493 | } else { |
---|
| 494 | dprintf("*** vga=current, not calling syslinux_force_text_mode()...\n"); |
---|
| 495 | } |
---|
| 496 | |
---|
| 497 | syslinux_shuffle_boot_rm(fraglist, mmap, 0, ®s); |
---|
| 498 | dprintf("shuffle_boot_rm failed\n"); |
---|
| 499 | |
---|
| 500 | bail: |
---|
| 501 | syslinux_free_movelist(fraglist); |
---|
| 502 | syslinux_free_memmap(mmap); |
---|
| 503 | syslinux_free_memmap(amap); |
---|
| 504 | return -1; |
---|
| 505 | } |
---|
| 506 | |
---|
| 507 | int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, |
---|
| 508 | struct initramfs *initramfs, |
---|
| 509 | struct setup_data *setup_data, |
---|
| 510 | char *cmdline) |
---|
| 511 | { |
---|
| 512 | if (firmware->boot_linux) |
---|
| 513 | return firmware->boot_linux(kernel_buf, kernel_size, initramfs, |
---|
| 514 | setup_data, cmdline); |
---|
| 515 | |
---|
| 516 | return bios_boot_linux(kernel_buf, kernel_size, initramfs, |
---|
| 517 | setup_data, cmdline); |
---|
| 518 | } |
---|