1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2011 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 * Load a Linux kernel (Image/zImage/bzImage).
42 #include <suffix_number.h>
43 #include <syslinux/align.h>
44 #include <syslinux/linux.h>
45 #include <syslinux/bootrm.h>
46 #include <syslinux/movebits.h>
50 uint8_t boot_sector_1[0x0020];
51 uint16_t old_cmd_line_magic;
52 uint16_t old_cmd_line_offset;
53 uint8_t boot_sector_2[0x01f1 - 0x0024];
64 uint32_t realmode_swtch;
66 uint16_t kernel_version;
67 uint8_t type_of_loader;
69 uint16_t setup_move_size;
70 uint32_t code32_start;
71 uint32_t ramdisk_image;
72 uint32_t ramdisk_size;
73 uint32_t bootsect_kludge;
74 uint16_t heap_end_ptr;
76 uint32_t cmd_line_ptr;
77 uint32_t initrd_addr_max;
78 uint32_t kernel_alignment;
79 uint8_t relocatable_kernel;
81 uint32_t cmdline_max_len;
82 uint32_t hardware_subarch;
83 uint64_t hardware_subarch_data;
84 uint32_t payload_offset;
85 uint32_t payload_length;
87 uint64_t pref_address;
91 #define BOOT_MAGIC 0xAA55
92 #define LINUX_MAGIC ('H' + ('d' << 8) + ('r' << 16) + ('S' << 24))
93 #define OLD_CMDLINE_MAGIC 0xA33F
96 #define LOAD_HIGH 0x01
97 #define CAN_USE_HEAP 0x80
100 * Find the last instance of a particular command line argument
101 * (which should include the final =; do not use for boolean arguments)
102 * Note: the resulting string is typically not null-terminated.
104 static const char *find_argument(const char *cmdline, const char *argument)
106 const char *found = NULL;
107 const char *p = cmdline;
108 bool was_space = true;
109 size_t la = strlen(argument);
114 } else if (was_space) {
115 if (!memcmp(p, argument, la))
125 /* Truncate to 32 bits, with saturate */
126 static inline uint32_t saturate32(unsigned long long v)
128 return (v > 0xffffffff) ? 0xffffffff : (uint32_t) v;
131 /* Get the combined size of the initramfs */
132 static addr_t initramfs_size(struct initramfs *initramfs)
134 struct initramfs *ip;
140 for (ip = initramfs->next; ip->len; ip = ip->next) {
141 size = (size + ip->align - 1) & ~(ip->align - 1); /* Alignment */
148 /* Create the appropriate mappings for the initramfs */
149 static int map_initramfs(struct syslinux_movelist **fraglist,
150 struct syslinux_memmap **mmap,
151 struct initramfs *initramfs, addr_t addr)
153 struct initramfs *ip;
154 addr_t next_addr, len, pad;
156 for (ip = initramfs->next; ip->len; ip = ip->next) {
158 next_addr = addr + len;
160 /* If this isn't the last entry, extend the zero-pad region
161 to enforce the alignment of the next chunk. */
163 pad = -next_addr & (ip->next->align - 1);
169 if (syslinux_add_movelist(fraglist, addr, (addr_t) ip->data, len))
172 if (len > ip->data_len) {
173 if (syslinux_add_memmap(mmap, addr + ip->data_len,
174 len - ip->data_len, SMT_ZERO))
183 int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
184 struct initramfs *initramfs,
185 struct setup_data *setup_data,
188 struct linux_header hdr, *whdr;
189 size_t real_mode_size, prot_mode_size;
190 addr_t real_mode_base, prot_mode_base;
192 size_t cmdline_size, cmdline_offset;
193 struct setup_data *sdp;
194 struct syslinux_rm_regs regs;
195 struct syslinux_movelist *fraglist = NULL;
196 struct syslinux_memmap *mmap = NULL;
197 struct syslinux_memmap *amap = NULL;
199 uint32_t memlimit = 0;
200 uint16_t video_mode = 0;
203 cmdline_size = strlen(cmdline) + 1;
205 if (kernel_size < 2 * 512)
208 /* Look for specific command-line arguments we care about */
209 if ((arg = find_argument(cmdline, "mem=")))
210 memlimit = saturate32(suffix_number(arg));
212 if ((arg = find_argument(cmdline, "vga="))) {
213 switch (arg[0] | 0x20) {
214 case 'a': /* "ask" */
217 case 'e': /* "ext" */
220 case 'n': /* "normal" */
223 case 'c': /* "current" */
227 video_mode = strtoul(arg, NULL, 0);
232 /* Copy the header into private storage */
233 /* Use whdr to modify the actual kernel header */
234 memcpy(&hdr, kernel_buf, sizeof hdr);
235 whdr = (struct linux_header *)kernel_buf;
237 if (hdr.boot_flag != BOOT_MAGIC)
240 if (hdr.header != LINUX_MAGIC) {
241 hdr.version = 0x0100; /* Very old kernel */
245 whdr->vid_mode = video_mode;
247 if (!hdr.setup_sects)
250 if (hdr.version < 0x0203)
251 hdr.initrd_addr_max = 0x37ffffff;
253 if (!memlimit && memlimit - 1 > hdr.initrd_addr_max)
254 memlimit = hdr.initrd_addr_max + 1; /* Zero for no limit */
256 if (hdr.version < 0x0205 || !(hdr.loadflags & LOAD_HIGH))
257 hdr.relocatable_kernel = 0;
259 if (hdr.version < 0x0206)
260 hdr.cmdline_max_len = 256;
262 if (cmdline_size > hdr.cmdline_max_len) {
263 cmdline_size = hdr.cmdline_max_len;
264 cmdline[cmdline_size - 1] = '\0';
267 if (hdr.version < 0x0202 || !(hdr.loadflags & 0x01))
268 cmdline_offset = (0x9ff0 - cmdline_size) & ~15;
270 cmdline_offset = 0x10000;
272 real_mode_size = (hdr.setup_sects + 1) << 9;
273 real_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x10000 : 0x90000;
274 prot_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x100000 : 0x10000;
275 prot_mode_size = kernel_size - real_mode_size;
277 if (hdr.version < 0x020a) {
279 * The 3* here is a total fudge factor... it's supposed to
280 * account for the fact that the kernel needs to be
281 * decompressed, and then followed by the BSS and BRK regions.
282 * This doesn't, however, account for the fact that the kernel
283 * is decompressed into a whole other place, either.
285 hdr.init_size = 3 * prot_mode_size;
288 if (!(hdr.loadflags & LOAD_HIGH) && prot_mode_size > 512 * 1024)
289 goto bail; /* Kernel cannot be loaded low */
291 if (initramfs && hdr.version < 0x0200)
292 goto bail; /* initrd/initramfs not supported */
294 if (hdr.version >= 0x0200) {
295 whdr->type_of_loader = 0x30; /* SYSLINUX unknown module */
296 if (hdr.version >= 0x0201) {
297 whdr->heap_end_ptr = cmdline_offset - 0x0200;
298 whdr->loadflags |= CAN_USE_HEAP;
300 if (hdr.version >= 0x0202) {
301 whdr->cmd_line_ptr = real_mode_base + cmdline_offset;
303 whdr->old_cmd_line_magic = OLD_CMDLINE_MAGIC;
304 whdr->old_cmd_line_offset = cmdline_offset;
305 /* Be paranoid and round up to a multiple of 16 */
306 whdr->setup_move_size = (cmdline_offset + cmdline_size + 15) & ~15;
310 /* Get the memory map */
311 mmap = syslinux_memory_map(); /* Memory map for shuffle_boot */
312 amap = syslinux_dup_memmap(mmap); /* Keep track of available memory */
316 dprintf("Initial memory map:\n");
317 syslinux_dump_memmap(mmap);
319 /* If the user has specified a memory limit, mark that as unavailable.
320 Question: should we mark this off-limit in the mmap as well (meaning
321 it's unavailable to the boot loader, which probably has already touched
322 some of it), or just in the amap? */
324 if (syslinux_add_memmap(&amap, memlimit, -memlimit, SMT_RESERVED))
327 /* Place the kernel in memory */
329 /* First, find a suitable place for the protected-mode code */
330 if (syslinux_memmap_type(amap, prot_mode_base, prot_mode_size)
332 const struct syslinux_memmap *mp;
333 if (!hdr.relocatable_kernel)
334 goto bail; /* Can't relocate - no hope */
337 for (mp = amap; mp; mp = mp->next) {
340 end = mp->next->start;
342 if (mp->type != SMT_FREE)
345 if (end <= prot_mode_base)
346 continue; /* Only relocate upwards */
348 if (start <= prot_mode_base)
349 start = prot_mode_base;
351 start = ALIGN_UP(start, hdr.kernel_alignment);
355 if (end - start >= hdr.init_size) {
356 whdr->code32_start += start - prot_mode_base;
357 prot_mode_base = start;
368 if (syslinux_memmap_type(amap, real_mode_base,
369 cmdline_offset + cmdline_size) != SMT_FREE) {
370 const struct syslinux_memmap *mp;
373 for (mp = amap; mp; mp = mp->next) {
376 end = mp->next->start;
378 if (mp->type != SMT_FREE)
381 if (start < real_mode_base)
382 start = real_mode_base; /* Lowest address we'll use */
383 if (end > 640 * 1024)
386 start = ALIGN_UP(start, 16);
387 if (start > 0x90000 || start >= end)
390 if (end - start >= cmdline_offset + cmdline_size) {
391 real_mode_base = start;
398 if (syslinux_add_movelist(&fraglist, real_mode_base, (addr_t) kernel_buf,
401 if (syslinux_add_memmap
402 (&amap, real_mode_base, cmdline_offset + cmdline_size, SMT_ALLOC))
405 /* Zero region between real mode code and cmdline */
406 if (syslinux_add_memmap(&mmap, real_mode_base + real_mode_size,
407 cmdline_offset - real_mode_size, SMT_ZERO))
411 if (syslinux_add_movelist(&fraglist, real_mode_base + cmdline_offset,
412 (addr_t) cmdline, cmdline_size))
415 /* Protected-mode code */
416 if (syslinux_add_movelist(&fraglist, prot_mode_base,
417 (addr_t) kernel_buf + real_mode_size,
420 if (syslinux_add_memmap(&amap, prot_mode_base, prot_mode_size, SMT_ALLOC))
423 /* Figure out the size of the initramfs, and where to put it.
424 We should put it at the highest possible address which is
425 <= hdr.initrd_addr_max, which fits the entire initramfs. */
427 irf_size = initramfs_size(initramfs); /* Handles initramfs == NULL */
430 addr_t best_addr = 0;
431 struct syslinux_memmap *ml;
432 const addr_t align_mask = INITRAMFS_MAX_ALIGN - 1;
435 for (ml = amap; ml->type != SMT_END; ml = ml->next) {
436 addr_t adj_start = (ml->start + align_mask) & ~align_mask;
437 addr_t adj_end = ml->next->start & ~align_mask;
438 if (ml->type == SMT_FREE && adj_end - adj_start >= irf_size)
439 best_addr = (adj_end - irf_size) & ~align_mask;
443 goto bail; /* Insufficient memory for initramfs */
445 whdr->ramdisk_image = best_addr;
446 whdr->ramdisk_size = irf_size;
448 if (syslinux_add_memmap(&amap, best_addr, irf_size, SMT_ALLOC))
451 if (map_initramfs(&fraglist, &mmap, initramfs, best_addr))
457 uint64_t *prev_ptr = &whdr->setup_data;
459 for (sdp = setup_data->next; sdp != setup_data; sdp = sdp->next) {
460 struct syslinux_memmap *ml;
461 const addr_t align_mask = 15; /* Header is 16 bytes */
462 addr_t best_addr = 0;
463 size_t size = sdp->hdr.len + sizeof(sdp->hdr);
465 if (hdr.version < 0x0209) {
466 /* Setup data not supported */
467 errno = ENXIO; /* Kind of arbitrary... */
471 if (!sdp->data || !sdp->hdr.len)
474 for (ml = amap; ml->type != SMT_END; ml = ml->next) {
475 addr_t adj_start = (ml->start + align_mask) & ~align_mask;
476 addr_t adj_end = ml->next->start & ~align_mask;
478 if (ml->type == SMT_FREE && adj_end - adj_start >= size)
479 best_addr = (adj_end - size) & ~align_mask;
485 *prev_ptr = best_addr;
486 prev_ptr = &sdp->hdr.next;
488 if (syslinux_add_memmap(&amap, best_addr, size, SMT_ALLOC))
490 if (syslinux_add_movelist(&fraglist, best_addr,
491 (addr_t)&sdp->hdr, sizeof sdp->hdr))
493 if (syslinux_add_movelist(&fraglist, best_addr + sizeof sdp->hdr,
494 (addr_t)sdp->data, sdp->hdr.len))
499 /* Set up the registers on entry */
500 memset(®s, 0, sizeof regs);
501 regs.es = regs.ds = regs.ss = regs.fs = regs.gs = real_mode_base >> 4;
502 regs.cs = (real_mode_base >> 4) + 0x20;
504 /* Linux is OK with sp = 0 = 64K, but perhaps other things aren't... */
505 regs.esp.w[0] = min(cmdline_offset, (size_t) 0xfff0);
507 dprintf("Final memory map:\n");
508 syslinux_dump_memmap(mmap);
510 dprintf("Final available map:\n");
511 syslinux_dump_memmap(amap);
513 dprintf("Initial movelist:\n");
514 syslinux_dump_movelist(fraglist);
516 syslinux_shuffle_boot_rm(fraglist, mmap, 0, ®s);
519 syslinux_free_movelist(fraglist);
520 syslinux_free_memmap(mmap);
521 syslinux_free_memmap(amap);