1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
4 *
5 *   Permission is hereby granted, free of charge, to any person
6 *   obtaining a copy of this software and associated documentation
7 *   files (the "Software"), to deal in the Software without
8 *   restriction, including without limitation the rights to use,
9 *   copy, modify, merge, publish, distribute, sublicense, and/or
10 *   sell copies of the Software, and to permit persons to whom
11 *   the Software is furnished to do so, subject to the following
12 *   conditions:
13 *
14 *   The above copyright notice and this permission notice shall
15 *   be included in all copies or substantial portions of the Software.
16 *
17 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 *   OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * ----------------------------------------------------------------------- */
27
28/*
29 * pmload.c
30 *
31 * Load a binary file and run it in protected mode.  We give it
32 * an ELF-style invocation record, becase, why not?
33 *
34 * Usage: pmload.c32 filename address [arguments...]
35 */
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <inttypes.h>
40#include <string.h>
41#include <fcntl.h>
42#include <unistd.h>
43#include <errno.h>
44#include <sys/stat.h>
45#include <elf.h>
46#include <console.h>
47#include <dprintf.h>
48
49#include <syslinux/loadfile.h>
50#include <syslinux/movebits.h>
51#include <syslinux/bootpm.h>
52
53/* If we don't have this much memory for the stack, signal failure */
54#define MIN_STACK	512
55
56static inline void error(const char *msg)
57{
58    fputs(msg, stderr);
59}
60
61int boot_raw(void *ptr, size_t len, addr_t where, char **argv)
62{
63    struct syslinux_movelist *ml = NULL;
64    struct syslinux_memmap *mmap = NULL, *amap = NULL;
65    struct syslinux_pm_regs regs;
66    int argc;
67    addr_t argsize;
68    char **argp;
69    addr_t lstart, llen;
70    char *stack_frame = NULL;
71    addr_t stack_frame_size;
72    addr_t stack_pointer;
73    uint32_t *spp;
74    char *sfp;
75    addr_t sfa;
76
77    memset(&regs, 0, sizeof regs);
78
79    mmap = syslinux_memory_map();
80    amap = syslinux_dup_memmap(mmap);
81    if (!mmap || !amap)
82	goto bail;
83
84    dprintf("Initial memory map:\n");
85    syslinux_dump_memmap(mmap);
86
87    dprintf("Segment at 0x%08x len 0x%08x\n", where, len);
88
89    if (syslinux_memmap_type(amap, where, len) != SMT_FREE) {
90	printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
91	       where, len);
92	goto bail;		/* Memory region unavailable */
93    }
94
95    /* Mark this region as allocated in the available map */
96    if (syslinux_add_memmap(&amap, where, len, SMT_ALLOC))
97	goto bail;
98
99    /* Data present region.  Create a move entry for it. */
100    if (syslinux_add_movelist(&ml, where, (addr_t) ptr, len))
101	goto bail;
102
103    /* Create the invocation record (initial stack frame) */
104
105    argsize = argc = 0;
106    for (argp = argv; *argp; argp++) {
107	dprintf("argv[%2d] = \"%s\"\n", argc, *argp);
108	argc++;
109	argsize += strlen(*argp) + 1;
110    }
111
112    /* We need the argument strings, argument pointers,
113       argc, plus four zero-word terminators. */
114    stack_frame_size = argsize + argc * sizeof(char *) + 5 * sizeof(long);
115    stack_frame_size = (stack_frame_size + 15) & ~15;
116    stack_frame = calloc(stack_frame_size, 1);
117    if (!stack_frame)
118	goto bail;
119
120    dprintf("Right before syslinux_memmap_largest()...\n");
121    syslinux_dump_memmap(amap);
122
123    if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen))
124	goto bail;		/* NO free memory?! */
125
126    if (llen < stack_frame_size + MIN_STACK + 16)
127	goto bail;		/* Insufficient memory  */
128
129    /* Initial stack pointer address */
130    stack_pointer = (lstart + llen - stack_frame_size) & ~15;
131
132    dprintf("Stack frame at 0x%08x len 0x%08x\n",
133	    stack_pointer, stack_frame_size);
134
135    /* Create the stack frame.  sfp is the pointer in current memory for
136       the next argument string, sfa is the address in its final resting place.
137       spp is the pointer into the argument array in current memory. */
138    spp = (uint32_t *) stack_frame;
139    sfp = stack_frame + argc * sizeof(char *) + 5 * sizeof(long);
140    sfa = stack_pointer + argc * sizeof(char *) + 5 * sizeof(long);
141
142    *spp++ = argc;
143    for (argp = argv; *argp; argp++) {
144	int bytes = strlen(*argp) + 1;	/* Including final null */
145	*spp++ = sfa;
146	memcpy(sfp, *argp, bytes);
147	sfp += bytes;
148	sfa += bytes;
149    }
150    /* Zero fields are aready taken care of by calloc() */
151
152    /* ... and we'll want to move it into the right place... */
153#if DEBUG
154    if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size)
155	!= SMT_FREE) {
156	dprintf("Stack frame area not free (how did that happen?)!\n");
157	goto bail;		/* Memory region unavailable */
158    }
159#endif
160
161    if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC))
162	goto bail;
163
164    if (syslinux_add_movelist(&ml, stack_pointer, (addr_t) stack_frame,
165			      stack_frame_size))
166	goto bail;
167
168    memset(&regs, 0, sizeof regs);
169    regs.eip = where;
170    regs.esp = stack_pointer;
171
172    dprintf("Final memory map:\n");
173    syslinux_dump_memmap(mmap);
174
175    dprintf("Final available map:\n");
176    syslinux_dump_memmap(amap);
177
178    dprintf("Movelist:\n");
179    syslinux_dump_movelist(ml);
180
181    /* This should not return... */
182    fputs("Booting...\n", stdout);
183    syslinux_shuffle_boot_pm(ml, mmap, 0, &regs);
184
185bail:
186    if (stack_frame)
187	free(stack_frame);
188    syslinux_free_memmap(amap);
189    syslinux_free_memmap(mmap);
190    syslinux_free_movelist(ml);
191
192    return -1;
193}
194
195int main(int argc, char *argv[])
196{
197    void *data;
198    size_t data_len;
199    addr_t where;
200
201    if (argc < 3) {
202	error("Usage: pmload.c32 bin_file address arguments...\n");
203	return 1;
204    }
205
206    where = strtoul(argv[2], NULL, 0);
207
208    if (loadfile(argv[1], &data, &data_len)) {
209	error("Unable to load file\n");
210	return 1;
211    }
212
213    boot_raw(data, data_len, where, &argv[1]);
214    error("Failed to boot, probably insufficient memory\n");
215    return 1;
216}
217