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>
52 #define BOOT_MAGIC 0xAA55
53 #define LINUX_MAGIC ('H' + ('d' << 8) + ('r' << 16) + ('S' << 24))
54 #define OLD_CMDLINE_MAGIC 0xA33F
57 #define LOAD_HIGH 0x01
58 #define CAN_USE_HEAP 0x80
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.
65 static const char *find_argument(const char *cmdline
, const char *argument
)
67 const char *found
= NULL
;
68 const char *p
= cmdline
;
69 bool was_space
= true;
70 size_t la
= strlen(argument
);
75 } else if (was_space
) {
76 if (!memcmp(p
, argument
, la
))
86 /* Truncate to 32 bits, with saturate */
87 static inline uint32_t saturate32(unsigned long long v
)
89 return (v
> 0xffffffff) ? 0xffffffff : (uint32_t) v
;
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
)
98 addr_t next_addr
, len
, pad
;
100 for (ip
= initramfs
->next
; ip
->len
; ip
= ip
->next
) {
102 next_addr
= addr
+ len
;
104 /* If this isn't the last entry, extend the zero-pad region
105 to enforce the alignment of the next chunk. */
107 pad
= -next_addr
& (ip
->next
->align
- 1);
113 if (syslinux_add_movelist(fraglist
, addr
, (addr_t
) ip
->data
, len
))
116 if (len
> ip
->data_len
) {
117 if (syslinux_add_memmap(mmap
, addr
+ ip
->data_len
,
118 len
- ip
->data_len
, SMT_ZERO
))
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
,
134 if (hdr
->version
>= 0x0202 && (hdr
->loadflags
& LOAD_HIGH
))
135 max_offset
= 0x10000;
137 max_offset
= 0xfff0 - cmdline_size
;
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)) {
145 return min(start
- base
, max_offset
) & ~15;
148 dprintf("Unable to find lowmem for cmdline\n");
149 return (0x9ff0 - cmdline_size
) & ~15; /* Legacy value: pure hope... */
152 int bios_boot_linux(void *kernel_buf
, size_t kernel_size
,
153 struct initramfs
*initramfs
,
154 struct setup_data
*setup_data
,
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
;
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;
171 cmdline_size
= strlen(cmdline
) + 1;
174 if (kernel_size
< 2 * 512) {
175 dprintf("Kernel size too small\n");
179 /* Look for specific command-line arguments we care about */
180 if ((arg
= find_argument(cmdline
, "mem=")))
181 memlimit
= saturate32(suffix_number(arg
));
183 if ((arg
= find_argument(cmdline
, "vga="))) {
184 switch (arg
[0] | 0x20) {
185 case 'a': /* "ask" */
188 case 'e': /* "ext" */
191 case 'n': /* "normal" */
194 case 'c': /* "current" */
198 video_mode
= strtoul(arg
, NULL
, 0);
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
;
208 if (hdr
.boot_flag
!= BOOT_MAGIC
) {
209 dprintf("Invalid boot magic\n");
213 if (hdr
.header
!= LINUX_MAGIC
) {
214 hdr
.version
= 0x0100; /* Very old kernel */
218 whdr
->vid_mode
= video_mode
;
220 if (!hdr
.setup_sects
)
223 if (hdr
.version
< 0x0203 || !hdr
.initrd_addr_max
)
224 hdr
.initrd_addr_max
= 0x37ffffff;
226 if (!memlimit
&& memlimit
- 1 > hdr
.initrd_addr_max
)
227 memlimit
= hdr
.initrd_addr_max
+ 1; /* Zero for no limit */
229 if (hdr
.version
< 0x0205 || !(hdr
.loadflags
& LOAD_HIGH
))
230 hdr
.relocatable_kernel
= 0;
232 if (hdr
.version
< 0x0206)
233 hdr
.cmdline_max_len
= 256;
235 if (cmdline_size
> hdr
.cmdline_max_len
) {
236 cmdline_size
= hdr
.cmdline_max_len
;
237 cmdline
[cmdline_size
- 1] = '\0';
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
;
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
) {
254 cmdline_offset
= calc_cmdline_offset(mmap
, &hdr
, cmdline_size
,
256 real_mode_base
+ real_mode_size
);
257 dprintf("cmdline_offset at 0x%x\n", real_mode_base
+ cmdline_offset
);
259 if (hdr
.version
< 0x020a) {
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.
267 hdr
.init_size
= 3 * prot_mode_size
;
270 if (!(hdr
.loadflags
& LOAD_HIGH
) && prot_mode_size
> 512 * 1024) {
271 dprintf("Kernel cannot be loaded low\n");
275 /* Get the size of the initramfs, if there is one */
276 irf_size
= initramfs_size(initramfs
);
278 if (irf_size
&& hdr
.version
< 0x0200) {
279 dprintf("Initrd specified but not supported by kernel\n");
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
;
291 dprintf("Initial memory map:\n");
292 syslinux_dump_memmap(mmap
);
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? */
299 if (syslinux_add_memmap(&amap
, memlimit
, -memlimit
, SMT_RESERVED
)) {
304 /* Place the kernel in memory */
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.
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");
325 whdr
->code32_start
+= base
- prot_mode_base
;
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");
335 if (syslinux_add_movelist(&fraglist
, real_mode_base
, (addr_t
) kernel_buf
,
338 if (syslinux_add_memmap
339 (&amap
, real_mode_base
, cmdline_offset
+ cmdline_size
, SMT_ALLOC
)) {
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
)) {
352 if (syslinux_add_movelist(&fraglist
, real_mode_base
+ cmdline_offset
,
353 (addr_t
) cmdline
, cmdline_size
)) {
357 if (hdr
.version
>= 0x0202) {
358 whdr
->cmd_line_ptr
= real_mode_base
+ cmdline_offset
;
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;
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
,
376 if (syslinux_add_memmap(&amap
, prot_mode_base
, prot_mode_size
,
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. */
388 addr_t best_addr
= 0;
389 struct syslinux_memmap
*ml
;
390 const addr_t align_mask
= INITRAMFS_MAX_ALIGN
- 1;
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
;
401 dprintf("Insufficient memory for initramfs\n");
405 whdr
->ramdisk_image
= best_addr
;
406 whdr
->ramdisk_size
= irf_size
;
408 if (syslinux_add_memmap(&amap
, best_addr
, irf_size
, SMT_ALLOC
)) {
413 if (map_initramfs(&fraglist
, &mmap
, initramfs
, best_addr
)) {
421 uint64_t *prev_ptr
= &whdr
->setup_data
;
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
);
429 if (!sdp
->data
|| !sdp
->hdr
.len
)
432 if (hdr
.version
< 0x0209) {
433 /* Setup data not supported */
434 errno
= ENXIO
; /* Kind of arbitrary... */
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
;
442 if (ml
->type
== SMT_FREE
&& adj_end
- adj_start
>= size
)
443 best_addr
= (adj_end
- size
) & ~align_mask
;
449 *prev_ptr
= best_addr
;
450 prev_ptr
= &sdp
->hdr
.next
;
452 if (syslinux_add_memmap(&amap
, best_addr
, size
, SMT_ALLOC
)) {
456 if (syslinux_add_movelist(&fraglist
, best_addr
,
457 (addr_t
)&sdp
->hdr
, sizeof sdp
->hdr
)) {
461 if (syslinux_add_movelist(&fraglist
, best_addr
+ sizeof sdp
->hdr
,
462 (addr_t
)sdp
->data
, sdp
->hdr
.len
)) {
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;
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);
477 dprintf("Final memory map:\n");
478 syslinux_dump_memmap(mmap
);
480 dprintf("Final available map:\n");
481 syslinux_dump_memmap(amap
);
483 dprintf("Initial movelist:\n");
484 syslinux_dump_movelist(fraglist
);
486 if (video_mode
!= 0x0f04) {
488 * video_mode is not "current", so if we are in graphics mode we
489 * need to revert to text mode...
491 dprintf("*** Calling syslinux_force_text_mode()...\n");
492 syslinux_force_text_mode();
494 dprintf("*** vga=current, not calling syslinux_force_text_mode()...\n");
497 syslinux_shuffle_boot_rm(fraglist
, mmap
, 0, ®s
);
498 dprintf("shuffle_boot_rm failed\n");
501 syslinux_free_movelist(fraglist
);
502 syslinux_free_memmap(mmap
);
503 syslinux_free_memmap(amap
);
507 int syslinux_boot_linux(void *kernel_buf
, size_t kernel_size
,
508 struct initramfs
*initramfs
,
509 struct setup_data
*setup_data
,
512 if (firmware
->boot_linux
)
513 return firmware
->boot_linux(kernel_buf
, kernel_size
, initramfs
,
514 setup_data
, cmdline
);
516 return bios_boot_linux(kernel_buf
, kernel_size
, initramfs
,
517 setup_data
, cmdline
);