1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009 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).
41 #include <syslinux/align.h>
42 #include <syslinux/linux.h>
43 #include <syslinux/bootrm.h>
44 #include <syslinux/movebits.h>
51 # define dprintf printf
53 # define dprintf(f, ...) ((void)0)
57 uint8_t boot_sector_1
[0x0020];
58 uint16_t old_cmd_line_magic
;
59 uint16_t old_cmd_line_offset
;
60 uint8_t boot_sector_2
[0x01f1 - 0x0024];
71 uint32_t realmode_swtch
;
73 uint16_t kernel_version
;
74 uint8_t type_of_loader
;
76 uint16_t setup_move_size
;
77 uint32_t code32_start
;
78 uint32_t ramdisk_image
;
79 uint32_t ramdisk_size
;
80 uint32_t bootsect_kludge
;
81 uint16_t heap_end_ptr
;
83 uint32_t cmd_line_ptr
;
84 uint32_t initrd_addr_max
;
85 uint32_t kernel_alignment
;
86 uint8_t relocatable_kernel
;
88 uint32_t cmdline_max_len
;
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
99 /* Get a value with a potential suffix (k/m/g/t/p/e) */
100 static unsigned long long suffix_number(const char *str
)
103 unsigned long long v
;
106 v
= strtoull(str
, &ep
, 0);
107 switch (*ep
| 0x20) {
136 * Find the last instance of a particular command line argument
137 * (which should include the final =; do not use for boolean arguments)
138 * Note: the resulting string is typically not null-terminated.
140 static const char *find_argument(const char *cmdline
, const char *argument
)
142 const char *found
= NULL
;
143 const char *p
= cmdline
;
144 bool was_space
= true;
145 size_t la
= strlen(argument
);
150 } else if (was_space
) {
151 if (!memcmp(p
, argument
, la
))
161 /* Truncate to 32 bits, with saturate */
162 static inline uint32_t saturate32(unsigned long long v
)
164 return (v
> 0xffffffff) ? 0xffffffff : (uint32_t) v
;
167 /* Get the combined size of the initramfs */
168 static addr_t
initramfs_size(struct initramfs
*initramfs
)
170 struct initramfs
*ip
;
176 for (ip
= initramfs
->next
; ip
->len
; ip
= ip
->next
) {
177 size
= (size
+ ip
->align
- 1) & ~(ip
->align
- 1); /* Alignment */
184 /* Create the appropriate mappings for the initramfs */
185 static int map_initramfs(struct syslinux_movelist
**fraglist
,
186 struct syslinux_memmap
**mmap
,
187 struct initramfs
*initramfs
, addr_t addr
)
189 struct initramfs
*ip
;
190 addr_t next_addr
, len
, pad
;
192 for (ip
= initramfs
->next
; ip
->len
; ip
= ip
->next
) {
194 next_addr
= addr
+ len
;
196 /* If this isn't the last entry, extend the zero-pad region
197 to enforce the alignment of the next chunk. */
199 pad
= -next_addr
& (ip
->next
->align
- 1);
205 if (syslinux_add_movelist(fraglist
, addr
, (addr_t
) ip
->data
, len
))
208 if (len
> ip
->data_len
) {
209 if (syslinux_add_memmap(mmap
, addr
+ ip
->data_len
,
210 len
- ip
->data_len
, SMT_ZERO
))
219 int syslinux_boot_linux(void *kernel_buf
, size_t kernel_size
,
220 struct initramfs
*initramfs
, char *cmdline
)
222 struct linux_header hdr
, *whdr
;
223 size_t real_mode_size
, prot_mode_size
;
224 addr_t real_mode_base
, prot_mode_base
;
226 size_t cmdline_size
, cmdline_offset
;
227 struct syslinux_rm_regs regs
;
228 struct syslinux_movelist
*fraglist
= NULL
;
229 struct syslinux_memmap
*mmap
= NULL
;
230 struct syslinux_memmap
*amap
= NULL
;
232 uint32_t memlimit
= 0;
233 uint16_t video_mode
= 0;
236 cmdline_size
= strlen(cmdline
) + 1;
238 if (kernel_size
< 2 * 512)
241 /* Look for specific command-line arguments we care about */
242 if ((arg
= find_argument(cmdline
, "mem=")))
243 memlimit
= saturate32(suffix_number(arg
));
245 if ((arg
= find_argument(cmdline
, "vga="))) {
246 switch (arg
[0] | 0x20) {
247 case 'a': /* "ask" */
250 case 'e': /* "ext" */
253 case 'n': /* "normal" */
256 case 'c': /* "current" */
260 video_mode
= strtoul(arg
, NULL
, 0);
265 /* Copy the header into private storage */
266 /* Use whdr to modify the actual kernel header */
267 memcpy(&hdr
, kernel_buf
, sizeof hdr
);
268 whdr
= (struct linux_header
*)kernel_buf
;
270 if (hdr
.boot_flag
!= BOOT_MAGIC
)
273 if (hdr
.header
!= LINUX_MAGIC
) {
274 hdr
.version
= 0x0100; /* Very old kernel */
278 whdr
->vid_mode
= video_mode
;
280 if (!hdr
.setup_sects
)
283 if (hdr
.version
< 0x0203)
284 hdr
.initrd_addr_max
= 0x37ffffff;
286 if (!memlimit
&& memlimit
- 1 > hdr
.initrd_addr_max
)
287 memlimit
= hdr
.initrd_addr_max
+ 1; /* Zero for no limit */
289 if (hdr
.version
< 0x0205 || !(hdr
.loadflags
& LOAD_HIGH
))
290 hdr
.relocatable_kernel
= 0;
292 if (hdr
.version
< 0x0206)
293 hdr
.cmdline_max_len
= 256;
295 if (cmdline_size
> hdr
.cmdline_max_len
) {
296 cmdline_size
= hdr
.cmdline_max_len
;
297 cmdline
[cmdline_size
- 1] = '\0';
300 if (hdr
.version
< 0x0202 || !(hdr
.loadflags
& 0x01))
301 cmdline_offset
= (0x9ff0 - cmdline_size
) & ~15;
303 cmdline_offset
= 0x10000;
305 real_mode_size
= (hdr
.setup_sects
+ 1) << 9;
306 real_mode_base
= (hdr
.loadflags
& LOAD_HIGH
) ? 0x10000 : 0x90000;
307 prot_mode_base
= (hdr
.loadflags
& LOAD_HIGH
) ? 0x100000 : 0x10000;
308 prot_mode_size
= kernel_size
- real_mode_size
;
310 if (!(hdr
.loadflags
& LOAD_HIGH
) && prot_mode_size
> 512 * 1024)
311 goto bail
; /* Kernel cannot be loaded low */
313 if (initramfs
&& hdr
.version
< 0x0200)
314 goto bail
; /* initrd/initramfs not supported */
316 if (hdr
.version
>= 0x0200) {
317 whdr
->type_of_loader
= 0x30; /* SYSLINUX unknown module */
318 if (hdr
.version
>= 0x0201) {
319 whdr
->heap_end_ptr
= cmdline_offset
- 0x0200;
320 whdr
->loadflags
|= CAN_USE_HEAP
;
322 if (hdr
.version
>= 0x0202) {
323 whdr
->cmd_line_ptr
= real_mode_base
+ cmdline_offset
;
325 whdr
->old_cmd_line_magic
= OLD_CMDLINE_MAGIC
;
326 whdr
->old_cmd_line_offset
= cmdline_offset
;
327 /* Be paranoid and round up to a multiple of 16 */
328 whdr
->setup_move_size
= (cmdline_offset
+ cmdline_size
+ 15) & ~15;
332 /* Get the memory map */
333 mmap
= syslinux_memory_map(); /* Memory map for shuffle_boot */
334 amap
= syslinux_dup_memmap(mmap
); /* Keep track of available memory */
339 dprintf("Initial memory map:\n");
340 syslinux_dump_memmap(stdout
, mmap
);
343 /* If the user has specified a memory limit, mark that as unavailable.
344 Question: should we mark this off-limit in the mmap as well (meaning
345 it's unavailable to the boot loader, which probably has already touched
346 some of it), or just in the amap? */
348 if (syslinux_add_memmap(&amap
, memlimit
, -memlimit
, SMT_RESERVED
))
351 /* Place the kernel in memory */
353 /* First, find a suitable place for the protected-mode code */
354 if (syslinux_memmap_type(amap
, prot_mode_base
, prot_mode_size
)
356 const struct syslinux_memmap
*mp
;
357 if (!hdr
.relocatable_kernel
)
358 goto bail
; /* Can't relocate - no hope */
361 for (mp
= amap
; mp
; mp
= mp
->next
) {
364 end
= mp
->next
->start
;
366 if (mp
->type
!= SMT_FREE
)
369 if (end
<= prot_mode_base
)
370 continue; /* Only relocate upwards */
372 if (start
<= prot_mode_base
)
373 start
= prot_mode_base
;
375 start
= ALIGN_UP(start
, hdr
.kernel_alignment
);
379 /* The 3* here is a total fudge factor... it's supposed to
380 account for the fact that the kernel needs to be decompressed,
381 and then followed by the BSS and BRK regions. This doesn't,
382 however, account for the fact that the kernel is decompressed
383 into a whole other place, either. */
384 if (end
- start
>= 3 * prot_mode_size
) {
385 whdr
->code32_start
+= start
- prot_mode_base
;
386 prot_mode_base
= start
;
397 if (syslinux_memmap_type(amap
, real_mode_base
,
398 cmdline_offset
+ cmdline_size
) != SMT_FREE
) {
399 const struct syslinux_memmap
*mp
;
402 for (mp
= amap
; mp
; mp
= mp
->next
) {
405 end
= mp
->next
->start
;
407 if (mp
->type
!= SMT_FREE
)
410 if (start
< real_mode_base
)
411 start
= real_mode_base
; /* Lowest address we'll use */
412 if (end
> 640 * 1024)
415 start
= ALIGN_UP(start
, 16);
416 if (start
> 0x90000 || start
>= end
)
419 if (end
- start
>= cmdline_offset
+ cmdline_size
) {
420 real_mode_base
= start
;
427 if (syslinux_add_movelist(&fraglist
, real_mode_base
, (addr_t
) kernel_buf
,
430 if (syslinux_add_memmap
431 (&amap
, real_mode_base
, cmdline_offset
+ cmdline_size
, SMT_ALLOC
))
434 /* Zero region between real mode code and cmdline */
435 if (syslinux_add_memmap(&mmap
, real_mode_base
+ real_mode_size
,
436 cmdline_offset
- real_mode_size
, SMT_ZERO
))
440 if (syslinux_add_movelist(&fraglist
, real_mode_base
+ cmdline_offset
,
441 (addr_t
) cmdline
, cmdline_size
))
444 /* Protected-mode code */
445 if (syslinux_add_movelist(&fraglist
, prot_mode_base
,
446 (addr_t
) kernel_buf
+ real_mode_size
,
449 if (syslinux_add_memmap(&amap
, prot_mode_base
, prot_mode_size
, SMT_ALLOC
))
452 /* Figure out the size of the initramfs, and where to put it.
453 We should put it at the highest possible address which is
454 <= hdr.initrd_addr_max, which fits the entire initramfs. */
456 irf_size
= initramfs_size(initramfs
); /* Handles initramfs == NULL */
459 addr_t best_addr
= 0;
460 struct syslinux_memmap
*ml
;
461 const addr_t align_mask
= INITRAMFS_MAX_ALIGN
- 1;
464 for (ml
= amap
; ml
->type
!= SMT_END
; ml
= ml
->next
) {
465 addr_t adj_start
= (ml
->start
+ align_mask
) & ~align_mask
;
466 addr_t adj_end
= ml
->next
->start
& ~align_mask
;
467 if (ml
->type
== SMT_FREE
&& adj_end
- adj_start
>= irf_size
)
468 best_addr
= (adj_end
- irf_size
) & ~align_mask
;
472 goto bail
; /* Insufficient memory for initramfs */
474 whdr
->ramdisk_image
= best_addr
;
475 whdr
->ramdisk_size
= irf_size
;
477 if (syslinux_add_memmap(&amap
, best_addr
, irf_size
, SMT_ALLOC
))
480 if (map_initramfs(&fraglist
, &mmap
, initramfs
, best_addr
))
485 /* Set up the registers on entry */
486 memset(®s
, 0, sizeof regs
);
487 regs
.es
= regs
.ds
= regs
.ss
= regs
.fs
= regs
.gs
= real_mode_base
>> 4;
488 regs
.cs
= (real_mode_base
>> 4) + 0x20;
490 /* Linux is OK with sp = 0 = 64K, but perhaps other things aren't... */
491 regs
.esp
.w
[0] = min(cmdline_offset
, (size_t) 0xfff0);
494 dprintf("Final memory map:\n");
495 syslinux_dump_memmap(stdout
, mmap
);
497 dprintf("Final available map:\n");
498 syslinux_dump_memmap(stdout
, amap
);
500 dprintf("Initial movelist:\n");
501 syslinux_dump_movelist(stdout
, fraglist
);
504 syslinux_shuffle_boot_rm(fraglist
, mmap
, 0, ®s
);
507 syslinux_free_movelist(fraglist
);
508 syslinux_free_memmap(mmap
);
509 syslinux_free_memmap(amap
);