1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2013 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>
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 #include <syslinux/config.h>
53 #define BOOT_MAGIC 0xAA55
54 #define LINUX_MAGIC ('H' + ('d' << 8) + ('r' << 16) + ('S' << 24))
55 #define OLD_CMDLINE_MAGIC 0xA33F
58 #define LOAD_HIGH 0x01
59 #define CAN_USE_HEAP 0x80
62 * Find the last instance of a particular command line argument
63 * (which should include the final =; do not use for boolean arguments)
64 * Note: the resulting string is typically not null-terminated.
66 static const char *find_argument(const char *cmdline
, const char *argument
)
68 const char *found
= NULL
;
69 const char *p
= cmdline
;
70 bool was_space
= true;
71 size_t la
= strlen(argument
);
76 } else if (was_space
) {
77 if (!memcmp(p
, argument
, la
))
87 /* Truncate to 32 bits, with saturate */
88 static inline uint32_t saturate32(unsigned long long v
)
90 return (v
> 0xffffffff) ? 0xffffffff : (uint32_t) v
;
93 /* Create the appropriate mappings for the initramfs */
94 static int map_initramfs(struct syslinux_movelist
**fraglist
,
95 struct syslinux_memmap
**mmap
,
96 struct initramfs
*initramfs
, addr_t addr
)
99 addr_t next_addr
, len
, pad
;
101 for (ip
= initramfs
->next
; ip
->len
; ip
= ip
->next
) {
103 next_addr
= addr
+ len
;
105 /* If this isn't the last entry, extend the zero-pad region
106 to enforce the alignment of the next chunk. */
108 pad
= -next_addr
& (ip
->next
->align
- 1);
114 if (syslinux_add_movelist(fraglist
, addr
, (addr_t
) ip
->data
, len
))
117 if (len
> ip
->data_len
) {
118 if (syslinux_add_memmap(mmap
, addr
+ ip
->data_len
,
119 len
- ip
->data_len
, SMT_ZERO
))
128 static size_t calc_cmdline_offset(const struct syslinux_memmap
*mmap
,
129 const struct linux_header
*hdr
,
130 size_t cmdline_size
, addr_t base
,
135 if (hdr
->version
>= 0x0202 && (hdr
->loadflags
& LOAD_HIGH
))
136 max_offset
= 0x10000;
138 max_offset
= 0xfff0 - cmdline_size
;
140 if (!syslinux_memmap_highest(mmap
, SMT_FREE
, &start
,
141 cmdline_size
, 0xa0000, 16) ||
142 !syslinux_memmap_highest(mmap
, SMT_TERMINAL
, &start
,
143 cmdline_size
, 0xa0000, 16)) {
146 return min(start
- base
, max_offset
) & ~15;
149 dprintf("Unable to find lowmem for cmdline\n");
150 return (0x9ff0 - cmdline_size
) & ~15; /* Legacy value: pure hope... */
153 int bios_boot_linux(void *kernel_buf
, size_t kernel_size
,
154 struct initramfs
*initramfs
,
155 struct setup_data
*setup_data
,
158 struct linux_header hdr
, *whdr
;
159 size_t real_mode_size
, prot_mode_size
;
160 addr_t real_mode_base
, prot_mode_base
, prot_mode_max
, base
;
162 size_t cmdline_size
, cmdline_offset
;
163 struct setup_data
*sdp
;
164 struct syslinux_rm_regs regs
;
165 struct syslinux_movelist
*fraglist
= NULL
;
166 struct syslinux_memmap
*mmap
= NULL
;
167 struct syslinux_memmap
*amap
= NULL
;
168 uint32_t memlimit
= 0;
169 uint16_t video_mode
= 0;
170 uint16_t bootflags
= 0;
173 cmdline_size
= strlen(cmdline
) + 1;
176 if (kernel_size
< 2 * 512) {
177 dprintf("Kernel size too small\n");
181 /* Look for specific command-line arguments we care about */
182 if ((arg
= find_argument(cmdline
, "mem=")))
183 memlimit
= saturate32(suffix_number(arg
));
185 if ((arg
= find_argument(cmdline
, "vga="))) {
186 switch (arg
[0] | 0x20) {
187 case 'a': /* "ask" */
190 case 'e': /* "ext" */
193 case 'n': /* "normal" */
196 case 'c': /* "current" */
200 video_mode
= strtoul(arg
, NULL
, 0);
205 if (syslinux_filesystem() == SYSLINUX_FS_PXELINUX
&&
206 strstr(cmdline
, "keeppxe")) {
207 extern __weak
char KeepPXE
;
209 KeepPXE
|= 1; /* for pxelinux_scan_memory */
210 bootflags
= 3; /* for unload_pxe */
213 /* Copy the header into private storage */
214 /* Use whdr to modify the actual kernel header */
215 memcpy(&hdr
, kernel_buf
, sizeof hdr
);
216 whdr
= (struct linux_header
*)kernel_buf
;
218 if (hdr
.boot_flag
!= BOOT_MAGIC
) {
219 dprintf("Invalid boot magic\n");
223 if (hdr
.header
!= LINUX_MAGIC
) {
224 hdr
.version
= 0x0100; /* Very old kernel */
228 whdr
->vid_mode
= video_mode
;
230 if (!hdr
.setup_sects
)
233 if (hdr
.version
< 0x0203 || !hdr
.initrd_addr_max
)
234 hdr
.initrd_addr_max
= 0x37ffffff;
236 if (!memlimit
&& memlimit
- 1 > hdr
.initrd_addr_max
)
237 memlimit
= hdr
.initrd_addr_max
+ 1; /* Zero for no limit */
239 if (hdr
.version
< 0x0205 || !(hdr
.loadflags
& LOAD_HIGH
))
240 hdr
.relocatable_kernel
= 0;
242 if (hdr
.version
< 0x0206)
243 hdr
.cmdline_max_len
= 256;
245 if (cmdline_size
> hdr
.cmdline_max_len
) {
246 cmdline_size
= hdr
.cmdline_max_len
;
247 cmdline
[cmdline_size
- 1] = '\0';
250 real_mode_size
= (hdr
.setup_sects
+ 1) << 9;
251 real_mode_base
= (hdr
.loadflags
& LOAD_HIGH
) ? 0x10000 : 0x90000;
252 prot_mode_base
= (hdr
.loadflags
& LOAD_HIGH
) ? 0x100000 : 0x10000;
253 prot_mode_max
= (hdr
.loadflags
& LOAD_HIGH
) ? (addr_t
)-1 : 0x8ffff;
254 prot_mode_size
= kernel_size
- real_mode_size
;
256 /* Get the memory map */
257 mmap
= syslinux_memory_map(); /* Memory map for shuffle_boot */
258 amap
= syslinux_dup_memmap(mmap
); /* Keep track of available memory */
259 if (!mmap
|| !amap
) {
264 cmdline_offset
= calc_cmdline_offset(mmap
, &hdr
, cmdline_size
,
266 real_mode_base
+ real_mode_size
);
267 dprintf("cmdline_offset at 0x%zx\n", real_mode_base
+ cmdline_offset
);
269 if (hdr
.version
< 0x020a) {
271 * The 3* here is a total fudge factor... it's supposed to
272 * account for the fact that the kernel needs to be
273 * decompressed, and then followed by the BSS and BRK regions.
274 * This doesn't, however, account for the fact that the kernel
275 * is decompressed into a whole other place, either.
277 hdr
.init_size
= 3 * prot_mode_size
;
280 if (!(hdr
.loadflags
& LOAD_HIGH
) && prot_mode_size
> 512 * 1024) {
281 dprintf("Kernel cannot be loaded low\n");
285 /* Get the size of the initramfs, if there is one */
286 irf_size
= initramfs_size(initramfs
);
288 if (irf_size
&& hdr
.version
< 0x0200) {
289 dprintf("Initrd specified but not supported by kernel\n");
293 if (hdr
.version
>= 0x0200) {
294 whdr
->type_of_loader
= 0x30; /* SYSLINUX unknown module */
295 if (hdr
.version
>= 0x0201) {
296 whdr
->heap_end_ptr
= cmdline_offset
- 0x0200;
297 whdr
->loadflags
|= CAN_USE_HEAP
;
301 dprintf("Initial memory map:\n");
302 syslinux_dump_memmap(mmap
);
304 /* If the user has specified a memory limit, mark that as unavailable.
305 Question: should we mark this off-limit in the mmap as well (meaning
306 it's unavailable to the boot loader, which probably has already touched
307 some of it), or just in the amap? */
309 if (syslinux_add_memmap(&amap
, memlimit
, -memlimit
, SMT_RESERVED
)) {
314 /* Place the kernel in memory */
317 * First, find a suitable place for the protected-mode code. If
318 * the kernel image is not relocatable, just worry if it fits (it
319 * might not even be a Linux image, after all, and for !LOAD_HIGH
320 * we end up decompressing into a different location anyway), but
321 * if it is, make sure everything fits.
323 base
= prot_mode_base
;
324 if (prot_mode_size
&&
325 syslinux_memmap_find(amap
, &base
,
326 hdr
.relocatable_kernel
?
327 hdr
.init_size
: prot_mode_size
,
328 hdr
.relocatable_kernel
, hdr
.kernel_alignment
,
329 prot_mode_base
, prot_mode_max
,
330 prot_mode_base
, prot_mode_max
)) {
331 dprintf("Could not find location for protected-mode code\n");
335 whdr
->code32_start
+= base
- prot_mode_base
;
336 prot_mode_base
= base
;
339 if (syslinux_memmap_find(amap
, &real_mode_base
,
340 cmdline_offset
+ cmdline_size
, true, 16,
341 real_mode_base
, 0x90000, 0, 640*1024)) {
342 dprintf("Could not find location for real-mode code\n");
346 if (syslinux_add_movelist(&fraglist
, real_mode_base
, (addr_t
) kernel_buf
,
349 if (syslinux_add_memmap
350 (&amap
, real_mode_base
, cmdline_offset
+ cmdline_size
, SMT_ALLOC
)) {
355 /* Zero region between real mode code and cmdline */
356 if (syslinux_add_memmap(&mmap
, real_mode_base
+ real_mode_size
,
357 cmdline_offset
- real_mode_size
, SMT_ZERO
)) {
363 if (syslinux_add_movelist(&fraglist
, real_mode_base
+ cmdline_offset
,
364 (addr_t
) cmdline
, cmdline_size
)) {
368 if (hdr
.version
>= 0x0202) {
369 whdr
->cmd_line_ptr
= real_mode_base
+ cmdline_offset
;
371 whdr
->old_cmd_line_magic
= OLD_CMDLINE_MAGIC
;
372 whdr
->old_cmd_line_offset
= cmdline_offset
;
373 if (hdr
.version
>= 0x0200) {
374 /* Be paranoid and round up to a multiple of 16 */
375 whdr
->setup_move_size
= (cmdline_offset
+ cmdline_size
+ 15) & ~15;
379 /* Protected-mode code */
380 if (prot_mode_size
) {
381 if (syslinux_add_movelist(&fraglist
, prot_mode_base
,
382 (addr_t
) kernel_buf
+ real_mode_size
,
387 if (syslinux_add_memmap(&amap
, prot_mode_base
, prot_mode_size
,
394 /* Figure out the size of the initramfs, and where to put it.
395 We should put it at the highest possible address which is
396 <= hdr.initrd_addr_max, which fits the entire initramfs. */
399 addr_t best_addr
= 0;
400 struct syslinux_memmap
*ml
;
401 const addr_t align_mask
= INITRAMFS_MAX_ALIGN
- 1;
404 for (ml
= amap
; ml
->type
!= SMT_END
; ml
= ml
->next
) {
405 addr_t adj_start
= (ml
->start
+ align_mask
) & ~align_mask
;
406 addr_t adj_end
= ml
->next
->start
& ~align_mask
;
407 if (ml
->type
== SMT_FREE
&& adj_end
- adj_start
>= irf_size
)
408 best_addr
= (adj_end
- irf_size
) & ~align_mask
;
412 dprintf("Insufficient memory for initramfs\n");
416 whdr
->ramdisk_image
= best_addr
;
417 whdr
->ramdisk_size
= irf_size
;
419 if (syslinux_add_memmap(&amap
, best_addr
, irf_size
, SMT_ALLOC
)) {
424 if (map_initramfs(&fraglist
, &mmap
, initramfs
, best_addr
)) {
432 uint64_t *prev_ptr
= &whdr
->setup_data
;
434 for (sdp
= setup_data
->next
; sdp
!= setup_data
; sdp
= sdp
->next
) {
435 struct syslinux_memmap
*ml
;
436 const addr_t align_mask
= 15; /* Header is 16 bytes */
437 addr_t best_addr
= 0;
438 size_t size
= sdp
->hdr
.len
+ sizeof(sdp
->hdr
);
440 if (!sdp
->data
|| !sdp
->hdr
.len
)
443 if (hdr
.version
< 0x0209) {
444 /* Setup data not supported */
445 errno
= ENXIO
; /* Kind of arbitrary... */
449 for (ml
= amap
; ml
->type
!= SMT_END
; ml
= ml
->next
) {
450 addr_t adj_start
= (ml
->start
+ align_mask
) & ~align_mask
;
451 addr_t adj_end
= ml
->next
->start
& ~align_mask
;
453 if (ml
->type
== SMT_FREE
&& adj_end
- adj_start
>= size
)
454 best_addr
= (adj_end
- size
) & ~align_mask
;
460 *prev_ptr
= best_addr
;
461 prev_ptr
= &sdp
->hdr
.next
;
463 if (syslinux_add_memmap(&amap
, best_addr
, size
, SMT_ALLOC
)) {
467 if (syslinux_add_movelist(&fraglist
, best_addr
,
468 (addr_t
)&sdp
->hdr
, sizeof sdp
->hdr
)) {
472 if (syslinux_add_movelist(&fraglist
, best_addr
+ sizeof sdp
->hdr
,
473 (addr_t
)sdp
->data
, sdp
->hdr
.len
)) {
480 /* Set up the registers on entry */
481 memset(®s
, 0, sizeof regs
);
482 regs
.es
= regs
.ds
= regs
.ss
= regs
.fs
= regs
.gs
= real_mode_base
>> 4;
483 regs
.cs
= (real_mode_base
>> 4) + 0x20;
485 /* Linux is OK with sp = 0 = 64K, but perhaps other things aren't... */
486 regs
.esp
.w
[0] = min(cmdline_offset
, (size_t) 0xfff0);
488 dprintf("Final memory map:\n");
489 syslinux_dump_memmap(mmap
);
491 dprintf("Final available map:\n");
492 syslinux_dump_memmap(amap
);
494 dprintf("Initial movelist:\n");
495 syslinux_dump_movelist(fraglist
);
497 if (video_mode
!= 0x0f04) {
499 * video_mode is not "current", so if we are in graphics mode we
500 * need to revert to text mode...
502 dprintf("*** Calling syslinux_force_text_mode()...\n");
503 syslinux_force_text_mode();
505 dprintf("*** vga=current, not calling syslinux_force_text_mode()...\n");
508 syslinux_shuffle_boot_rm(fraglist
, mmap
, bootflags
, ®s
);
509 dprintf("shuffle_boot_rm failed\n");
512 syslinux_free_movelist(fraglist
);
513 syslinux_free_memmap(mmap
);
514 syslinux_free_memmap(amap
);
518 int syslinux_boot_linux(void *kernel_buf
, size_t kernel_size
,
519 struct initramfs
*initramfs
,
520 struct setup_data
*setup_data
,
523 if (firmware
->boot_linux
)
524 return firmware
->boot_linux(kernel_buf
, kernel_size
, initramfs
,
525 setup_data
, cmdline
);
527 return bios_boot_linux(kernel_buf
, kernel_size
, initramfs
,
528 setup_data
, cmdline
);