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(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%08x 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
;
109 unsigned int i
, mbh_offset
;
113 * Search for the multiboot header...
116 for (mbh_offset
= 0; mbh_offset
< MULTIBOOT_SEARCH
; mbh_offset
+= 4) {
117 mbh
= (struct multiboot_header
*)((char *)ptr
+ mbh_offset
);
118 if (mbh
->magic
!= MULTIBOOT_MAGIC
)
120 if (mbh
->magic
+ mbh
->flags
+ mbh
->checksum
)
122 if (mbh
->flags
& MULTIBOOT_VIDEO_MODE
)
124 else if (mbh
->flags
& MULTIBOOT_AOUT_KLUDGE
)
129 if (mbh_offset
+ mbh_len
> len
)
130 mbh_len
= 0; /* Invalid... */
132 break; /* Found something... */
136 bad_flags
= mbh
->flags
& MULTIBOOT_UNSUPPORTED
;
138 printf("Unsupported Multiboot flags set: %#x\n", bad_flags
);
143 if (len
< sizeof(Elf32_Ehdr
) ||
144 memcmp(eh
->e_ident
, "\x7f" "ELF\1\1\1", 6) ||
145 (eh
->e_machine
!= EM_386
&& eh
->e_machine
!= EM_486
&&
146 eh
->e_machine
!= EM_X86_64
) ||
147 eh
->e_version
!= EV_CURRENT
||
148 eh
->e_ehsize
< sizeof(Elf32_Ehdr
) || eh
->e_ehsize
>= len
||
149 eh
->e_phentsize
< sizeof(Elf32_Phdr
) ||
150 !eh
->e_phnum
|| eh
->e_phoff
+ eh
->e_phentsize
* eh
->e_phnum
> len
)
151 eh
= NULL
; /* No valid ELF header found */
153 /* Is this a Solaris kernel? */
154 if (!set
.solaris
&& eh
&& kernel_is_solaris(eh
))
158 * Note: the Multiboot Specification implies that AOUT_KLUDGE should
159 * have precedence over the ELF header. However, Grub disagrees, and
160 * Grub is "the reference bootloader" for the Multiboot Specification.
161 * This is insane, since it makes the AOUT_KLUDGE bit functionally
162 * useless, but at least Solaris apparently depends on this behavior.
164 if (eh
&& !(opt
.aout
&& mbh_len
&& (mbh
->flags
& MULTIBOOT_AOUT_KLUDGE
))) {
165 regs
.eip
= eh
->e_entry
; /* Can be overridden further down... */
167 ph
= (Elf32_Phdr
*) (cptr
+ eh
->e_phoff
);
169 for (i
= 0; i
< eh
->e_phnum
; i
++) {
170 if (ph
->p_type
== PT_LOAD
|| ph
->p_type
== PT_PHDR
) {
172 * This loads at p_paddr, which matches Grub. However, if
173 * e_entry falls within the p_vaddr range of this PHDR, then
174 * adjust it to match the p_paddr range... this is how Grub
175 * behaves, so it's by definition correct (it doesn't have to
178 addr_t addr
= ph
->p_paddr
;
179 addr_t msize
= ph
->p_memsz
;
180 addr_t dsize
= min(msize
, ph
->p_filesz
);
182 if (eh
->e_entry
>= ph
->p_vaddr
183 && eh
->e_entry
< ph
->p_vaddr
+ msize
)
184 regs
.eip
= eh
->e_entry
+ (ph
->p_paddr
- ph
->p_vaddr
);
186 dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
189 if (syslinux_memmap_type(amap
, addr
, msize
) != SMT_FREE
) {
191 ("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
193 return NULL
; /* Memory region unavailable */
196 /* Mark this region as allocated in the available map */
197 if (syslinux_add_memmap(&amap
, addr
, msize
, SMT_ALLOC
)) {
198 error("Overlapping segments found in ELF header\n");
203 /* Data present region. Create a move entry for it. */
204 if (syslinux_add_movelist
205 (&ml
, addr
, (addr_t
) cptr
+ ph
->p_offset
, dsize
)) {
206 error("Failed to map PHDR data\n");
211 /* Zero-filled region. Mark as a zero region in the memory map. */
212 if (syslinux_add_memmap
213 (&mmap
, addr
+ dsize
, msize
- dsize
, SMT_ZERO
)) {
214 error("Failed to map PHDR zero region\n");
218 if (addr
+ msize
> mboot_high_water_mark
)
219 mboot_high_water_mark
= addr
+ msize
;
221 /* Ignore this program header */
224 ph
= (Elf32_Phdr
*) ((char *)ph
+ eh
->e_phentsize
);
227 /* Load the ELF symbol table */
231 sh
= (Elf32_Shdr
*) ((char *)eh
+ eh
->e_shoff
);
233 len
= eh
->e_shentsize
* eh
->e_shnum
;
235 * Align this, but don't pad -- in general this means a bunch of
236 * smaller sections gets packed into a single page.
238 addr
= map_data(sh
, len
, 4096, MAP_HIGH
| MAP_NOPAD
);
240 error("Failed to map symbol table\n");
244 mbinfo
.flags
|= MB_INFO_ELF_SHDR
;
245 mbinfo
.syms
.e
.addr
= addr
;
246 mbinfo
.syms
.e
.num
= eh
->e_shnum
;
247 mbinfo
.syms
.e
.size
= eh
->e_shentsize
;
248 mbinfo
.syms
.e
.shndx
= eh
->e_shstrndx
;
250 for (i
= 0; i
< eh
->e_shnum
; i
++) {
254 continue; /* Empty section */
255 if (sh
[i
].sh_flags
& SHF_ALLOC
)
256 continue; /* SHF_ALLOC sections should have PHDRs */
258 align
= sh
[i
].sh_addralign
? sh
[i
].sh_addralign
: 0;
259 addr
= map_data((char *)ptr
+ sh
[i
].sh_offset
, sh
[i
].sh_size
,
262 error("Failed to map symbol section\n");
265 sh
[i
].sh_addr
= addr
;
268 } else if (mbh_len
&& (mbh
->flags
& MULTIBOOT_AOUT_KLUDGE
)) {
270 * a.out kludge thing...
273 addr_t data_len
, bss_len
;
276 regs
.eip
= mbh
->entry_addr
;
278 data_ptr
= (char *)mbh
- (mbh
->header_addr
- mbh
->load_addr
);
280 if (mbh
->load_end_addr
)
281 data_len
= mbh
->load_end_addr
- mbh
->load_addr
;
283 data_len
= len
- mbh_offset
+ (mbh
->header_addr
- mbh
->load_addr
);
285 bss_addr
= mbh
->load_addr
+ data_len
;
287 if (mbh
->bss_end_addr
)
288 bss_len
= mbh
->bss_end_addr
- mbh
->load_end_addr
;
292 if (syslinux_memmap_type(amap
, mbh
->load_addr
, data_len
+ bss_len
)
294 printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
295 mbh
->load_addr
, data_len
+ bss_len
);
296 return NULL
; /* Memory region unavailable */
298 if (syslinux_add_memmap(&amap
, mbh
->load_addr
,
299 data_len
+ bss_len
, SMT_ALLOC
)) {
300 error("Failed to claim a.out address space!\n");
304 if (syslinux_add_movelist(&ml
, mbh
->load_addr
, (addr_t
) data_ptr
,
306 error("Failed to map a.out data\n");
310 if (syslinux_add_memmap
311 (&mmap
, bss_addr
, bss_len
, SMT_ZERO
)) {
312 error("Failed to map a.out bss\n");
315 if (bss_addr
+ bss_len
> mboot_high_water_mark
)
316 mboot_high_water_mark
= bss_addr
+ bss_len
;
319 ("Invalid Multiboot image: neither ELF header nor a.out kludge found\n");
327 * Set up a stack. This isn't actually required by the spec, but it seems
328 * like a prudent thing to do. Also, put enough zeros at the top of the
329 * stack that something that looks for an ELF invocation record will know
332 static void mboot_map_stack(void)
336 if (syslinux_memmap_largest(amap
, SMT_FREE
, &start
, &len
) || len
< 64)
337 return; /* Not much we can do, here... */
339 regs
.esp
= (start
+ len
- 32) & ~15;
340 dprintf("Mapping stack at 0x%08x\n", regs
.esp
);
341 syslinux_add_memmap(&mmap
, regs
.esp
, 32, SMT_ZERO
);
344 void mboot_run(int bootflags
)
348 dprintf("Running, eip = 0x%08x, ebx = 0x%08x\n", regs
.eip
, regs
.ebx
);
350 regs
.eax
= MULTIBOOT_VALID
;
351 syslinux_shuffle_boot_pm(ml
, mmap
, bootflags
, ®s
);