• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5  *
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
13  *   conditions:
14  *
15  *   The above copyright notice and this permission notice shall
16  *   be included in all copies or substantial portions of the Software.
17  *
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.
26  *
27  * ----------------------------------------------------------------------- */
28 
29 /*
30  * map.c
31  *
32  * Functions that deal with the memory map of various objects
33  */
34 
35 #include "mboot.h"
36 
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;
40 
41 /*
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.
46  *
47  * As a precaution, this also pads the data with zero up to the next
48  * alignment datum.
49  */
map_data(const void * data,size_t len,size_t align,int flags)50 addr_t map_data(const void *data, size_t len, size_t align, int flags)
51 {
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;
55 
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);
61 	return 0;
62     }
63 
64     dprintf("Mapping 0x%08x bytes (%#x pad) at 0x%08x\n", len, pad, start);
65 
66     if (start + len + pad > mboot_high_water_mark)
67 	mboot_high_water_mark = start + len + pad;
68 
69     return start;
70 }
71 
map_string(const char * string)72 addr_t map_string(const char *string)
73 {
74     if (!string)
75 	return 0;
76     else
77 	return map_data(string, strlen(string) + 1, 1, 0);
78 }
79 
init_map(void)80 int init_map(void)
81 {
82     /*
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
86      * allocated.
87      */
88     mmap = syslinux_memory_map();
89     amap = syslinux_dup_memmap(mmap);
90     if (!mmap || !amap) {
91 	error("Failed to allocate initial memory map!\n");
92 	return -1;
93     }
94 
95     dprintf("Initial memory map:\n");
96     syslinux_dump_memmap(mmap);
97 
98     return 0;
99 }
100 
map_image(void * ptr,size_t len)101 struct multiboot_header *map_image(void *ptr, size_t len)
102 {
103     struct multiboot_header *mbh;
104     int mbh_len;
105     char *cptr = ptr;
106     Elf32_Ehdr *eh = ptr;
107     Elf32_Phdr *ph;
108     Elf32_Shdr *sh;
109     unsigned int i, mbh_offset;
110     uint32_t bad_flags;
111 
112     /*
113      * Search for the multiboot header...
114      */
115     mbh_len = 0;
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)
119 	    continue;
120 	if (mbh->magic + mbh->flags + mbh->checksum)
121 	    continue;
122 	if (mbh->flags & MULTIBOOT_VIDEO_MODE)
123 	    mbh_len = 48;
124 	else if (mbh->flags & MULTIBOOT_AOUT_KLUDGE)
125 	    mbh_len = 32;
126 	else
127 	    mbh_len = 12;
128 
129 	if (mbh_offset + mbh_len > len)
130 	    mbh_len = 0;	/* Invalid... */
131 	else
132 	    break;		/* Found something... */
133     }
134 
135     if (mbh_len) {
136 	bad_flags = mbh->flags & MULTIBOOT_UNSUPPORTED;
137 	if (bad_flags) {
138 	    printf("Unsupported Multiboot flags set: %#x\n", bad_flags);
139 	    return NULL;
140 	}
141     }
142 
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 */
152 
153     /* Is this a Solaris kernel? */
154     if (!set.solaris && eh && kernel_is_solaris(eh))
155 	opt.solaris = true;
156 
157     /*
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.
163      */
164     if (eh && !(opt.aout && mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE))) {
165 	regs.eip = eh->e_entry;	/* Can be overridden further down... */
166 
167 	ph = (Elf32_Phdr *) (cptr + eh->e_phoff);
168 
169 	for (i = 0; i < eh->e_phnum; i++) {
170 	    if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
171 		/*
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
176 		 * make sense...)
177 		 */
178 		addr_t addr = ph->p_paddr;
179 		addr_t msize = ph->p_memsz;
180 		addr_t dsize = min(msize, ph->p_filesz);
181 
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);
185 
186 		dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
187 			addr, dsize, msize);
188 
189 		if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) {
190 		    printf
191 			("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
192 			 addr, msize);
193 		    return NULL;	/* Memory region unavailable */
194 		}
195 
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");
199 		    return NULL;
200 		}
201 
202 		if (ph->p_filesz) {
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");
207 			return NULL;
208 		    }
209 		}
210 		if (msize > dsize) {
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");
215 			return NULL;
216 		    }
217 		}
218 		if (addr + msize > mboot_high_water_mark)
219 		    mboot_high_water_mark = addr + msize;
220 	    } else {
221 		/* Ignore this program header */
222 	    }
223 
224 	    ph = (Elf32_Phdr *) ((char *)ph + eh->e_phentsize);
225 	}
226 
227 	/* Load the ELF symbol table */
228 	if (eh->e_shoff) {
229 	    addr_t addr, len;
230 
231 	    sh = (Elf32_Shdr *) ((char *)eh + eh->e_shoff);
232 
233 	    len = eh->e_shentsize * eh->e_shnum;
234 	    /*
235 	     * Align this, but don't pad -- in general this means a bunch of
236 	     * smaller sections gets packed into a single page.
237 	     */
238 	    addr = map_data(sh, len, 4096, MAP_HIGH | MAP_NOPAD);
239 	    if (!addr) {
240 		error("Failed to map symbol table\n");
241 		return NULL;
242 	    }
243 
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;
249 
250 	    for (i = 0; i < eh->e_shnum; i++) {
251 		addr_t align;
252 
253 		if (!sh[i].sh_size)
254 		    continue;	/* Empty section */
255 		if (sh[i].sh_flags & SHF_ALLOC)
256 		    continue;	/* SHF_ALLOC sections should have PHDRs */
257 
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,
260 				align, MAP_HIGH);
261 		if (!addr) {
262 		    error("Failed to map symbol section\n");
263 		    return NULL;
264 		}
265 		sh[i].sh_addr = addr;
266 	    }
267 	}
268     } else if (mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE)) {
269 	/*
270 	 * a.out kludge thing...
271 	 */
272 	char *data_ptr;
273 	addr_t data_len, bss_len;
274 	addr_t bss_addr;
275 
276 	regs.eip = mbh->entry_addr;
277 
278 	data_ptr = (char *)mbh - (mbh->header_addr - mbh->load_addr);
279 
280 	if (mbh->load_end_addr)
281 	    data_len = mbh->load_end_addr - mbh->load_addr;
282 	else
283 	    data_len = len - mbh_offset + (mbh->header_addr - mbh->load_addr);
284 
285 	bss_addr = mbh->load_addr + data_len;
286 
287 	if (mbh->bss_end_addr)
288 	    bss_len = mbh->bss_end_addr - mbh->load_end_addr;
289 	else
290 	    bss_len = 0;
291 
292 	if (syslinux_memmap_type(amap, mbh->load_addr, data_len + bss_len)
293 	    != SMT_FREE) {
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 */
297 	}
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");
301 	    return NULL;
302 	}
303 	if (data_len)
304 	    if (syslinux_add_movelist(&ml, mbh->load_addr, (addr_t) data_ptr,
305 				      data_len)) {
306 		error("Failed to map a.out data\n");
307 		return NULL;
308 	    }
309 	if (bss_len)
310 	    if (syslinux_add_memmap
311 		(&mmap, bss_addr, bss_len, SMT_ZERO)) {
312 		error("Failed to map a.out bss\n");
313 		return NULL;
314 	    }
315 	if (bss_addr + bss_len > mboot_high_water_mark)
316 	    mboot_high_water_mark = bss_addr + bss_len;
317     } else {
318 	error
319 	    ("Invalid Multiboot image: neither ELF header nor a.out kludge found\n");
320 	return NULL;
321     }
322 
323     return mbh;
324 }
325 
326 /*
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
330  * there isn't one.
331  */
mboot_map_stack(void)332 static void mboot_map_stack(void)
333 {
334     addr_t start, len;
335 
336     if (syslinux_memmap_largest(amap, SMT_FREE, &start, &len) || len < 64)
337 	return;			/* Not much we can do, here... */
338 
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);
342 }
343 
mboot_run(int bootflags)344 void mboot_run(int bootflags)
345 {
346     mboot_map_stack();
347 
348     dprintf("Running, eip = 0x%08x, ebx = 0x%08x\n", regs.eip, regs.ebx);
349 
350     regs.eax = MULTIBOOT_VALID;
351     syslinux_shuffle_boot_pm(ml, mmap, bootflags, &regs);
352 }
353