1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 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 * Functions that deal with the memory map of various objects
37 static struct syslinux_movelist
*ml
= NULL
;
38 static struct syslinux_memmap
*mmap
= NULL
, *amap
= NULL
;
39 static addr_t mboot_high_water_mark
= 0x100000;
42 * Note: although there is no such thing in the spec, at least Xen makes
43 * assumptions as to where in the memory space Grub would have loaded
44 * certain things. To support that, if "high" is set, then allocate this
45 * at an address strictly above any previous allocations.
47 * As a precaution, this also pads the data with zero up to the next
50 addr_t
map_data(const void *data
, size_t len
, size_t align
, int flags
)
52 addr_t start
= (flags
& MAP_HIGH
) ? mboot_high_water_mark
: 0x2000;
53 addr_t pad
= (flags
& MAP_NOPAD
) ? 0 : -len
& (align
- 1);
54 addr_t xlen
= len
+ pad
;
56 if (syslinux_memmap_find_type(amap
, SMT_FREE
, &start
, &xlen
, align
) ||
57 syslinux_add_memmap(&amap
, start
, len
+ pad
, SMT_ALLOC
) ||
58 syslinux_add_movelist(&ml
, start
, (addr_t
) data
, len
) ||
59 (pad
&& syslinux_add_memmap(&mmap
, start
+ len
, pad
, SMT_ZERO
))) {
60 printf("Cannot map %zu bytes\n", len
+ pad
);
64 dprintf("Mapping 0x%08zx bytes (%#x pad) at 0x%08x\n", len
, pad
, start
);
66 if (start
+ len
+ pad
> mboot_high_water_mark
)
67 mboot_high_water_mark
= start
+ len
+ pad
;
72 addr_t
map_string(const char *string
)
77 return map_data(string
, strlen(string
) + 1, 1, 0);
83 * Note: mmap is the memory map (containing free and zeroed regions)
84 * needed by syslinux_shuffle_boot_pm(); amap is a map where we keep
85 * track ourselves which target memory ranges have already been
88 mmap
= syslinux_memory_map();
89 amap
= syslinux_dup_memmap(mmap
);
91 error("Failed to allocate initial memory map!\n");
95 dprintf("Initial memory map:\n");
96 syslinux_dump_memmap(mmap
);
101 struct multiboot_header
*map_image(void *ptr
, size_t len
)
103 struct multiboot_header
*mbh
;
106 Elf32_Ehdr
*eh
= ptr
;
110 Elf64_Ehdr
*eh64
= ptr
;
114 unsigned int i
, mbh_offset
;
118 * Search for the multiboot header...
121 for (mbh_offset
= 0; mbh_offset
< MULTIBOOT_SEARCH
; mbh_offset
+= 4) {
122 mbh
= (struct multiboot_header
*)((char *)ptr
+ mbh_offset
);
123 if (mbh
->magic
!= MULTIBOOT_MAGIC
)
125 if (mbh
->magic
+ mbh
->flags
+ mbh
->checksum
)
127 if (mbh
->flags
& MULTIBOOT_VIDEO_MODE
)
129 else if (mbh
->flags
& MULTIBOOT_AOUT_KLUDGE
)
134 if (mbh_offset
+ mbh_len
> len
)
135 mbh_len
= 0; /* Invalid... */
137 break; /* Found something... */
141 bad_flags
= mbh
->flags
& MULTIBOOT_UNSUPPORTED
;
143 printf("Unsupported Multiboot flags set: %#x\n", bad_flags
);
148 if (len
< sizeof(Elf32_Ehdr
) ||
149 memcmp(eh
->e_ident
, "\x7f" "ELF\1\1\1", 6) ||
150 (eh
->e_machine
!= EM_386
&& eh
->e_machine
!= EM_486
&&
151 eh
->e_machine
!= EM_X86_64
) ||
152 eh
->e_version
!= EV_CURRENT
||
153 eh
->e_ehsize
< sizeof(Elf32_Ehdr
) || eh
->e_ehsize
>= len
||
154 eh
->e_phentsize
< sizeof(Elf32_Phdr
) ||
155 !eh
->e_phnum
|| eh
->e_phoff
+ eh
->e_phentsize
* eh
->e_phnum
> len
)
156 eh
= NULL
; /* No valid ELF header found */
158 /* Determine 64-bit images */
160 len
< sizeof(Elf64_Ehdr
) ||
161 memcmp(eh64
->e_ident
, "\x7f" "ELF\2\1\1", 6) ||
162 (eh64
->e_machine
!= EM_X86_64
) ||
163 eh64
->e_version
!= EV_CURRENT
||
164 eh64
->e_ehsize
< sizeof(Elf64_Ehdr
) || eh64
->e_ehsize
>= len
||
165 eh64
->e_phentsize
< sizeof(Elf64_Phdr
) ||
167 eh64
->e_phoff
+ eh64
->e_phentsize
* eh64
->e_phnum
> len
)
168 eh64
= NULL
; /* No valid ELF64 header found */
170 /* Is this a Solaris kernel? */
171 if (!set
.solaris
&& eh
&& kernel_is_solaris(eh
))
175 * Note: the Multiboot Specification implies that AOUT_KLUDGE should
176 * have precedence over the ELF header. However, Grub disagrees, and
177 * Grub is "the reference bootloader" for the Multiboot Specification.
178 * This is insane, since it makes the AOUT_KLUDGE bit functionally
179 * useless, but at least Solaris apparently depends on this behavior.
181 if (eh
&& !(opt
.aout
&& mbh_len
&& (mbh
->flags
& MULTIBOOT_AOUT_KLUDGE
))) {
182 regs
.eip
= eh
->e_entry
; /* Can be overridden further down... */
184 ph
= (Elf32_Phdr
*) (cptr
+ eh
->e_phoff
);
186 for (i
= 0; i
< eh
->e_phnum
; i
++) {
187 if (ph
->p_type
== PT_LOAD
|| ph
->p_type
== PT_PHDR
) {
189 * This loads at p_paddr, which matches Grub. However, if
190 * e_entry falls within the p_vaddr range of this PHDR, then
191 * adjust it to match the p_paddr range... this is how Grub
192 * behaves, so it's by definition correct (it doesn't have to
195 addr_t addr
= ph
->p_paddr
;
196 addr_t msize
= ph
->p_memsz
;
197 addr_t dsize
= min(msize
, ph
->p_filesz
);
199 if (eh
->e_entry
>= ph
->p_vaddr
200 && eh
->e_entry
< ph
->p_vaddr
+ msize
)
201 regs
.eip
= eh
->e_entry
+ (ph
->p_paddr
- ph
->p_vaddr
);
203 dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
206 if (syslinux_memmap_type(amap
, addr
, msize
) != SMT_FREE
) {
208 ("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
210 return NULL
; /* Memory region unavailable */
213 /* Mark this region as allocated in the available map */
214 if (syslinux_add_memmap(&amap
, addr
, msize
, SMT_ALLOC
)) {
215 error("Overlapping segments found in ELF header\n");
220 /* Data present region. Create a move entry for it. */
221 if (syslinux_add_movelist
222 (&ml
, addr
, (addr_t
) cptr
+ ph
->p_offset
, dsize
)) {
223 error("Failed to map PHDR data\n");
228 /* Zero-filled region. Mark as a zero region in the memory map. */
229 if (syslinux_add_memmap
230 (&mmap
, addr
+ dsize
, msize
- dsize
, SMT_ZERO
)) {
231 error("Failed to map PHDR zero region\n");
235 if (addr
+ msize
> mboot_high_water_mark
)
236 mboot_high_water_mark
= addr
+ msize
;
238 /* Ignore this program header */
241 ph
= (Elf32_Phdr
*) ((char *)ph
+ eh
->e_phentsize
);
244 /* Load the ELF symbol table */
248 sh
= (Elf32_Shdr
*) ((char *)eh
+ eh
->e_shoff
);
250 len
= eh
->e_shentsize
* eh
->e_shnum
;
252 * Align this, but don't pad -- in general this means a bunch of
253 * smaller sections gets packed into a single page.
255 addr
= map_data(sh
, len
, 4096, MAP_HIGH
| MAP_NOPAD
);
257 error("Failed to map symbol table\n");
261 mbinfo
.flags
|= MB_INFO_ELF_SHDR
;
262 mbinfo
.syms
.e
.addr
= addr
;
263 mbinfo
.syms
.e
.num
= eh
->e_shnum
;
264 mbinfo
.syms
.e
.size
= eh
->e_shentsize
;
265 mbinfo
.syms
.e
.shndx
= eh
->e_shstrndx
;
267 for (i
= 0; i
< eh
->e_shnum
; i
++) {
271 continue; /* Empty section */
272 if (sh
[i
].sh_flags
& SHF_ALLOC
)
273 continue; /* SHF_ALLOC sections should have PHDRs */
275 align
= sh
[i
].sh_addralign
? sh
[i
].sh_addralign
: 0;
276 addr
= map_data((char *)ptr
+ sh
[i
].sh_offset
, sh
[i
].sh_size
,
279 error("Failed to map symbol section\n");
282 sh
[i
].sh_addr
= addr
;
285 } else if (eh64
&& !(opt
.aout
&& mbh_len
&&
286 (mbh
->flags
& MULTIBOOT_AOUT_KLUDGE
))) {
287 /* Load 64-bit ELF */
288 regs
.eip
= eh64
->e_entry
; /* Can be overridden further down... */
290 ph64
= (Elf64_Phdr
*) (cptr
+ eh64
->e_phoff
);
292 for (i
= 0; i
< eh64
->e_phnum
; i
++) {
293 if (ph64
->p_type
== PT_LOAD
|| ph64
->p_type
== PT_PHDR
) {
295 * This loads at p_paddr, which matches Grub. However, if
296 * e_entry falls within the p_vaddr range of this PHDR, then
297 * adjust it to match the p_paddr range... this is how Grub
298 * behaves, so it's by definition correct (it doesn't have to
301 addr_t addr
= ph64
->p_paddr
;
302 addr_t msize
= ph64
->p_memsz
;
303 addr_t dsize
= min(msize
, ph64
->p_filesz
);
305 if (eh64
->e_entry
>= ph64
->p_vaddr
306 && eh64
->e_entry
< ph64
->p_vaddr
+ msize
)
307 regs
.eip
= eh64
->e_entry
+ (ph64
->p_paddr
- ph64
->p_vaddr
);
309 dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
312 if (syslinux_memmap_type(amap
, addr
, msize
) != SMT_FREE
) {
314 ("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
316 return NULL
; /* Memory region unavailable */
319 /* Mark this region as allocated in the available map */
320 if (syslinux_add_memmap(&amap
, addr
, msize
, SMT_ALLOC
)) {
321 error("Overlapping segments found in ELF header\n");
325 if (ph64
->p_filesz
) {
326 /* Data present region. Create a move entry for it. */
327 if (syslinux_add_movelist
328 (&ml
, addr
, (addr_t
) cptr
+ ph64
->p_offset
, dsize
)) {
329 error("Failed to map PHDR data\n");
334 /* Zero-filled region. Mark as a zero region in the memory map. */
335 if (syslinux_add_memmap
336 (&mmap
, addr
+ dsize
, msize
- dsize
, SMT_ZERO
)) {
337 error("Failed to map PHDR zero region\n");
341 if (addr
+ msize
> mboot_high_water_mark
)
342 mboot_high_water_mark
= addr
+ msize
;
344 /* Ignore this program header */
347 ph64
= (Elf64_Phdr
*) ((char *)ph64
+ eh64
->e_phentsize
);
350 /* Load the ELF symbol table */
354 sh64
= (Elf64_Shdr
*) ((char *)eh64
+ eh64
->e_shoff
);
356 len
= eh64
->e_shentsize
* eh64
->e_shnum
;
358 * Align this, but don't pad -- in general this means a bunch of
359 * smaller sections gets packed into a single page.
361 addr
= map_data(sh64
, len
, 4096, MAP_HIGH
| MAP_NOPAD
);
363 error("Failed to map symbol table\n");
367 mbinfo
.flags
|= MB_INFO_ELF_SHDR
;
368 mbinfo
.syms
.e
.addr
= addr
;
369 mbinfo
.syms
.e
.num
= eh64
->e_shnum
;
370 mbinfo
.syms
.e
.size
= eh64
->e_shentsize
;
371 mbinfo
.syms
.e
.shndx
= eh64
->e_shstrndx
;
373 for (i
= 0; i
< eh64
->e_shnum
; i
++) {
376 if (!sh64
[i
].sh_size
)
377 continue; /* Empty section */
378 if (sh64
[i
].sh_flags
& SHF_ALLOC
)
379 continue; /* SHF_ALLOC sections should have PHDRs */
381 align
= sh64
[i
].sh_addralign
? sh64
[i
].sh_addralign
: 0;
382 addr
= map_data((char *)ptr
+ sh64
[i
].sh_offset
,
383 sh64
[i
].sh_size
, align
, MAP_HIGH
);
385 error("Failed to map symbol section\n");
388 sh64
[i
].sh_addr
= addr
;
391 } else if (mbh_len
&& (mbh
->flags
& MULTIBOOT_AOUT_KLUDGE
)) {
393 * a.out kludge thing...
396 addr_t data_len
, bss_len
;
399 regs
.eip
= mbh
->entry_addr
;
401 data_ptr
= (char *)mbh
- (mbh
->header_addr
- mbh
->load_addr
);
403 if (mbh
->load_end_addr
)
404 data_len
= mbh
->load_end_addr
- mbh
->load_addr
;
406 data_len
= len
- mbh_offset
+ (mbh
->header_addr
- mbh
->load_addr
);
408 bss_addr
= mbh
->load_addr
+ data_len
;
410 if (mbh
->bss_end_addr
)
411 bss_len
= mbh
->bss_end_addr
- mbh
->load_end_addr
;
415 if (syslinux_memmap_type(amap
, mbh
->load_addr
, data_len
+ bss_len
)
417 printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
418 mbh
->load_addr
, data_len
+ bss_len
);
419 return NULL
; /* Memory region unavailable */
421 if (syslinux_add_memmap(&amap
, mbh
->load_addr
,
422 data_len
+ bss_len
, SMT_ALLOC
)) {
423 error("Failed to claim a.out address space!\n");
427 if (syslinux_add_movelist(&ml
, mbh
->load_addr
, (addr_t
) data_ptr
,
429 error("Failed to map a.out data\n");
433 if (syslinux_add_memmap
434 (&mmap
, bss_addr
, bss_len
, SMT_ZERO
)) {
435 error("Failed to map a.out bss\n");
438 if (bss_addr
+ bss_len
> mboot_high_water_mark
)
439 mboot_high_water_mark
= bss_addr
+ bss_len
;
442 ("Invalid Multiboot image: neither ELF header nor a.out kludge found\n");
450 * Set up a stack. This isn't actually required by the spec, but it seems
451 * like a prudent thing to do. Also, put enough zeros at the top of the
452 * stack that something that looks for an ELF invocation record will know
455 static void mboot_map_stack(void)
459 if (syslinux_memmap_largest(amap
, SMT_FREE
, &start
, &len
) || len
< 64)
460 return; /* Not much we can do, here... */
462 regs
.esp
= (start
+ len
- 32) & ~15;
463 dprintf("Mapping stack at 0x%08x\n", regs
.esp
);
464 syslinux_add_memmap(&mmap
, regs
.esp
, 32, SMT_ZERO
);
467 void mboot_run(int bootflags
)
471 dprintf("Running, eip = 0x%08x, ebx = 0x%08x\n", regs
.eip
, regs
.ebx
);
473 regs
.eax
= MULTIBOOT_VALID
;
474 syslinux_shuffle_boot_pm(ml
, mmap
, bootflags
, ®s
);