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>
46 #include <syslinux/align.h>
47 #include <syslinux/linux.h>
48 #include <syslinux/bootrm.h>
49 #include <syslinux/movebits.h>
50 #include <syslinux/firmware.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 int bios_boot_linux(void *kernel_buf
, size_t kernel_size
,
128 struct initramfs
*initramfs
,
129 struct setup_data
*setup_data
,
132 struct linux_header hdr
, *whdr
;
133 size_t real_mode_size
, prot_mode_size
;
134 addr_t real_mode_base
, prot_mode_base
;
136 size_t cmdline_size
, cmdline_offset
;
137 struct setup_data
*sdp
;
138 struct syslinux_rm_regs regs
;
139 struct syslinux_movelist
*fraglist
= NULL
;
140 struct syslinux_memmap
*mmap
= NULL
;
141 struct syslinux_memmap
*amap
= NULL
;
143 uint32_t memlimit
= 0;
144 uint16_t video_mode
= 0;
147 cmdline_size
= strlen(cmdline
) + 1;
150 if (kernel_size
< 2 * 512)
153 /* Look for specific command-line arguments we care about */
154 if ((arg
= find_argument(cmdline
, "mem=")))
155 memlimit
= saturate32(suffix_number(arg
));
157 if ((arg
= find_argument(cmdline
, "vga="))) {
158 switch (arg
[0] | 0x20) {
159 case 'a': /* "ask" */
162 case 'e': /* "ext" */
165 case 'n': /* "normal" */
168 case 'c': /* "current" */
172 video_mode
= strtoul(arg
, NULL
, 0);
177 /* Copy the header into private storage */
178 /* Use whdr to modify the actual kernel header */
179 memcpy(&hdr
, kernel_buf
, sizeof hdr
);
180 whdr
= (struct linux_header
*)kernel_buf
;
182 if (hdr
.boot_flag
!= BOOT_MAGIC
)
185 if (hdr
.header
!= LINUX_MAGIC
) {
186 hdr
.version
= 0x0100; /* Very old kernel */
190 whdr
->vid_mode
= video_mode
;
192 if (!hdr
.setup_sects
)
195 if (hdr
.version
< 0x0203)
196 hdr
.initrd_addr_max
= 0x37ffffff;
198 if (!memlimit
&& memlimit
- 1 > hdr
.initrd_addr_max
)
199 memlimit
= hdr
.initrd_addr_max
+ 1; /* Zero for no limit */
201 if (hdr
.version
< 0x0205 || !(hdr
.loadflags
& LOAD_HIGH
))
202 hdr
.relocatable_kernel
= 0;
204 if (hdr
.version
< 0x0206)
205 hdr
.cmdline_max_len
= 256;
207 if (cmdline_size
> hdr
.cmdline_max_len
) {
208 cmdline_size
= hdr
.cmdline_max_len
;
209 cmdline
[cmdline_size
- 1] = '\0';
212 if (hdr
.version
< 0x0202 || !(hdr
.loadflags
& 0x01))
213 cmdline_offset
= (0x9ff0 - cmdline_size
) & ~15;
215 cmdline_offset
= 0x10000;
217 real_mode_size
= (hdr
.setup_sects
+ 1) << 9;
218 real_mode_base
= (hdr
.loadflags
& LOAD_HIGH
) ? 0x10000 : 0x90000;
219 prot_mode_base
= (hdr
.loadflags
& LOAD_HIGH
) ? 0x100000 : 0x10000;
220 prot_mode_size
= kernel_size
- real_mode_size
;
222 if (hdr
.version
< 0x020a) {
224 * The 3* here is a total fudge factor... it's supposed to
225 * account for the fact that the kernel needs to be
226 * decompressed, and then followed by the BSS and BRK regions.
227 * This doesn't, however, account for the fact that the kernel
228 * is decompressed into a whole other place, either.
230 hdr
.init_size
= 3 * prot_mode_size
;
233 if (!(hdr
.loadflags
& LOAD_HIGH
) && prot_mode_size
> 512 * 1024)
234 goto bail
; /* Kernel cannot be loaded low */
236 /* Get the size of the initramfs, if there is one */
237 irf_size
= initramfs_size(initramfs
);
239 if (irf_size
&& hdr
.version
< 0x0200)
240 goto bail
; /* initrd/initramfs not supported */
242 if (hdr
.version
>= 0x0200) {
243 whdr
->type_of_loader
= 0x30; /* SYSLINUX unknown module */
244 if (hdr
.version
>= 0x0201) {
245 whdr
->heap_end_ptr
= cmdline_offset
- 0x0200;
246 whdr
->loadflags
|= CAN_USE_HEAP
;
250 /* Get the memory map */
251 mmap
= syslinux_memory_map(); /* Memory map for shuffle_boot */
252 amap
= syslinux_dup_memmap(mmap
); /* Keep track of available memory */
253 if (!mmap
|| !amap
) {
258 dprintf("Initial memory map:\n");
259 syslinux_dump_memmap(mmap
);
261 /* If the user has specified a memory limit, mark that as unavailable.
262 Question: should we mark this off-limit in the mmap as well (meaning
263 it's unavailable to the boot loader, which probably has already touched
264 some of it), or just in the amap? */
266 if (syslinux_add_memmap(&amap
, memlimit
, -memlimit
, SMT_RESERVED
)) {
271 /* Place the kernel in memory */
273 /* First, find a suitable place for the protected-mode code */
274 if (prot_mode_size
&&
275 syslinux_memmap_type(amap
, prot_mode_base
, prot_mode_size
)
277 const struct syslinux_memmap
*mp
;
278 if (!hdr
.relocatable_kernel
)
279 goto bail
; /* Can't relocate - no hope */
282 for (mp
= amap
; mp
; mp
= mp
->next
) {
285 end
= mp
->next
->start
;
287 if (mp
->type
!= SMT_FREE
)
290 if (end
<= prot_mode_base
)
291 continue; /* Only relocate upwards */
293 if (start
<= prot_mode_base
)
294 start
= prot_mode_base
;
296 start
= ALIGN_UP(start
, hdr
.kernel_alignment
);
300 if (end
- start
>= hdr
.init_size
) {
301 whdr
->code32_start
+= start
- prot_mode_base
;
302 prot_mode_base
= start
;
313 if (syslinux_memmap_type(amap
, real_mode_base
,
314 cmdline_offset
+ cmdline_size
) != SMT_FREE
) {
315 const struct syslinux_memmap
*mp
;
318 for (mp
= amap
; mp
; mp
= mp
->next
) {
321 end
= mp
->next
->start
;
323 if (mp
->type
!= SMT_FREE
)
326 if (start
< real_mode_base
)
327 start
= real_mode_base
; /* Lowest address we'll use */
328 if (end
> 640 * 1024)
331 start
= ALIGN_UP(start
, 16);
332 if (start
> 0x90000 || start
>= end
)
335 if (end
- start
>= cmdline_offset
+ cmdline_size
) {
336 real_mode_base
= start
;
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 goto bail
; /* Insufficient memory for initramfs */
414 whdr
->ramdisk_image
= best_addr
;
415 whdr
->ramdisk_size
= irf_size
;
417 if (syslinux_add_memmap(&amap
, best_addr
, irf_size
, SMT_ALLOC
)) {
422 if (map_initramfs(&fraglist
, &mmap
, initramfs
, best_addr
)) {
430 uint64_t *prev_ptr
= &whdr
->setup_data
;
432 for (sdp
= setup_data
->next
; sdp
!= setup_data
; sdp
= sdp
->next
) {
433 struct syslinux_memmap
*ml
;
434 const addr_t align_mask
= 15; /* Header is 16 bytes */
435 addr_t best_addr
= 0;
436 size_t size
= sdp
->hdr
.len
+ sizeof(sdp
->hdr
);
438 if (!sdp
->data
|| !sdp
->hdr
.len
)
441 if (hdr
.version
< 0x0209) {
442 /* Setup data not supported */
443 errno
= ENXIO
; /* Kind of arbitrary... */
447 for (ml
= amap
; ml
->type
!= SMT_END
; ml
= ml
->next
) {
448 addr_t adj_start
= (ml
->start
+ align_mask
) & ~align_mask
;
449 addr_t adj_end
= ml
->next
->start
& ~align_mask
;
451 if (ml
->type
== SMT_FREE
&& adj_end
- adj_start
>= size
)
452 best_addr
= (adj_end
- size
) & ~align_mask
;
458 *prev_ptr
= best_addr
;
459 prev_ptr
= &sdp
->hdr
.next
;
461 if (syslinux_add_memmap(&amap
, best_addr
, size
, SMT_ALLOC
)) {
465 if (syslinux_add_movelist(&fraglist
, best_addr
,
466 (addr_t
)&sdp
->hdr
, sizeof sdp
->hdr
)) {
470 if (syslinux_add_movelist(&fraglist
, best_addr
+ sizeof sdp
->hdr
,
471 (addr_t
)sdp
->data
, sdp
->hdr
.len
)) {
478 /* Set up the registers on entry */
479 memset(®s
, 0, sizeof regs
);
480 regs
.es
= regs
.ds
= regs
.ss
= regs
.fs
= regs
.gs
= real_mode_base
>> 4;
481 regs
.cs
= (real_mode_base
>> 4) + 0x20;
483 /* Linux is OK with sp = 0 = 64K, but perhaps other things aren't... */
484 regs
.esp
.w
[0] = min(cmdline_offset
, (size_t) 0xfff0);
486 dprintf("Final memory map:\n");
487 syslinux_dump_memmap(mmap
);
489 dprintf("Final available map:\n");
490 syslinux_dump_memmap(amap
);
492 dprintf("Initial movelist:\n");
493 syslinux_dump_movelist(fraglist
);
495 if (video_mode
!= 0x0f04) {
497 * video_mode is not "current", so if we are in graphics mode we
498 * need to revert to text mode...
500 dprintf("*** Calling syslinux_force_text_mode()...\n");
501 syslinux_force_text_mode();
503 dprintf("*** vga=current, not calling syslinux_force_text_mode()...\n");
506 syslinux_shuffle_boot_rm(fraglist
, mmap
, 0, ®s
);
509 syslinux_free_movelist(fraglist
);
510 syslinux_free_memmap(mmap
);
511 syslinux_free_memmap(amap
);
515 int syslinux_boot_linux(void *kernel_buf
, size_t kernel_size
,
516 struct initramfs
*initramfs
,
517 struct setup_data
*setup_data
,
520 if (firmware
->boot_linux
)
521 return firmware
->boot_linux(kernel_buf
, kernel_size
, initramfs
,
522 setup_data
, cmdline
);
524 return bios_boot_linux(kernel_buf
, kernel_size
, initramfs
,
525 setup_data
, cmdline
);