1 /*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation;
5 * version 2.1 of the License.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
14 *
15 * Some of the field descriptions were copied from "The Multiboot
16 * Specification", Copyright 1995, 96 Bryan Ford <baford@cs.utah.edu>,
17 * Erich Stefan Boleyn <erich@uruk.org> Copyright 1999, 2000, 2001, 2002
18 * Free Software Foundation, Inc.
19 */
20
21 /******************************************************************************
22 *
23 * Loads simple binary images. It's like a .COM file in MS-DOS. No headers are
24 * present. The only requirement is that it must have a xen_bin_image table
25 * somewhere in the first 8192 bytes, starting on a 32-bit aligned address.
26 * Those familiar with the multiboot specification should recognize this, it's
27 * (almost) the same as the multiboot header.
28 * The layout of the xen_bin_image table is:
29 *
30 * Offset Type Name Note
31 * 0 uint32_t magic required
32 * 4 uint32_t flags required
33 * 8 uint32_t checksum required
34 * 12 uint32_t header_addr required
35 * 16 uint32_t load_addr required
36 * 20 uint32_t load_end_addr required
37 * 24 uint32_t bss_end_addr required
38 * 28 uint32_t entry_addr required
39 *
40 * - magic
41 * Magic number identifying the table. For images to be loaded by Xen 3, the
42 * magic value is 0x336ec578 ("xEn3" with the 0x80 bit of the "E" set).
43 * - flags
44 * bit 0: indicates whether the image needs to be loaded on a page boundary
45 * bit 1: reserved, must be 0 (the multiboot spec uses this bit to indicate
46 * that memory info should be passed to the image)
47 * bit 2: reserved, must be 0 (the multiboot spec uses this bit to indicate
48 * that the bootloader should pass video mode info to the image)
49 * bit 16: reserved, must be 1 (the multiboot spec uses this bit to indicate
50 * that the values in the fields header_addr - entry_addr are
51 * valid)
52 * All other bits should be set to 0.
53 * - checksum
54 * When added to "magic" and "flags", the resulting value should be 0.
55 * - header_addr
56 * Contains the virtual address corresponding to the beginning of the
57 * table - the memory location at which the magic value is supposed to be
58 * loaded. This field serves to synchronize the mapping between OS image
59 * offsets and virtual memory addresses.
60 * - load_addr
61 * Contains the virtual address of the beginning of the text segment. The
62 * offset in the OS image file at which to start loading is defined by the
63 * offset at which the table was found, minus (header addr - load addr).
64 * load addr must be less than or equal to header addr.
65 * - load_end_addr
66 * Contains the virtual address of the end of the data segment.
67 * (load_end_addr - load_addr) specifies how much data to load. This implies
68 * that the text and data segments must be consecutive in the OS image. If
69 * this field is zero, the domain builder assumes that the text and data
70 * segments occupy the whole OS image file.
71 * - bss_end_addr
72 * Contains the virtual address of the end of the bss segment. The domain
73 * builder initializes this area to zero, and reserves the memory it occupies
74 * to avoid placing boot modules and other data relevant to the loaded image
75 * in that area. If this field is zero, the domain builder assumes that no bss
76 * segment is present.
77 * - entry_addr
78 * The virtual address at which to start execution of the loaded image.
79 *
80 */
81
82 #include <stdlib.h>
83 #include <inttypes.h>
84
85 #include "xg_private.h"
86 #include "xc_dom.h"
87
88 #define round_pgup(_p) (((_p)+(PAGE_SIZE_X86-1))&PAGE_MASK_X86)
89 #define round_pgdown(_p) ((_p)&PAGE_MASK_X86)
90
91 struct xen_bin_image_table
92 {
93 uint32_t magic;
94 uint32_t flags;
95 uint32_t checksum;
96 uint32_t header_addr;
97 uint32_t load_addr;
98 uint32_t load_end_addr;
99 uint32_t bss_end_addr;
100 uint32_t entry_addr;
101 };
102
103 #define XEN_MULTIBOOT_MAGIC3 0x336ec578
104
105 #define XEN_MULTIBOOT_FLAG_ALIGN4K 0x00000001
106 #define XEN_MULTIBOOT_FLAG_NEEDMEMINFO 0x00000002
107 #define XEN_MULTIBOOT_FLAG_NEEDVIDINFO 0x00000004
108 #define XEN_MULTIBOOT_FLAG_ADDRSVALID 0x00010000
109 #define XEN_MULTIBOOT_FLAG_PAE_SHIFT 14
110 #define XEN_MULTIBOOT_FLAG_PAE_MASK (3 << XEN_MULTIBOOT_FLAG_PAE_SHIFT)
111
112 /* Flags we test for */
113 #define FLAGS_MASK ((~ 0) & (~ XEN_MULTIBOOT_FLAG_ALIGN4K) & \
114 (~ XEN_MULTIBOOT_FLAG_PAE_MASK))
115 #define FLAGS_REQUIRED XEN_MULTIBOOT_FLAG_ADDRSVALID
116
117 /* --------------------------------------------------------------------- */
118
find_table(struct xc_dom_image * dom)119 static struct xen_bin_image_table *find_table(struct xc_dom_image *dom)
120 {
121 struct xen_bin_image_table *table;
122 uint32_t *probe_ptr;
123 uint32_t *probe_end;
124
125 if ( dom->kernel_size < sizeof(*table) )
126 return NULL;
127 probe_ptr = dom->kernel_blob;
128 if ( dom->kernel_size > (8192 + sizeof(*table)) )
129 probe_end = dom->kernel_blob + 8192;
130 else
131 probe_end = dom->kernel_blob + dom->kernel_size - sizeof(*table);
132
133 for ( table = NULL; probe_ptr < probe_end; probe_ptr++ )
134 {
135 if ( *probe_ptr == XEN_MULTIBOOT_MAGIC3 )
136 {
137 table = (struct xen_bin_image_table *) probe_ptr;
138 /* Checksum correct? */
139 if ( (table->magic + table->flags + table->checksum) == 0 )
140 return table;
141 }
142 }
143 return NULL;
144 }
145
xc_dom_probe_bin_kernel(struct xc_dom_image * dom)146 static int xc_dom_probe_bin_kernel(struct xc_dom_image *dom)
147 {
148 return find_table(dom) ? 0 : -EINVAL;
149 }
150
xc_dom_parse_bin_kernel(struct xc_dom_image * dom)151 static int xc_dom_parse_bin_kernel(struct xc_dom_image *dom)
152 {
153 struct xen_bin_image_table *image_info;
154 char *image = dom->kernel_blob;
155 size_t image_size = dom->kernel_size;
156 uint32_t start_addr;
157 uint32_t load_end_addr;
158 uint32_t bss_end_addr;
159 uint32_t pae_flags;
160
161 image_info = find_table(dom);
162 if ( !image_info )
163 return -EINVAL;
164
165 DOMPRINTF("%s: multiboot header fields", __FUNCTION__);
166 DOMPRINTF(" flags: 0x%" PRIx32 "", image_info->flags);
167 DOMPRINTF(" header_addr: 0x%" PRIx32 "", image_info->header_addr);
168 DOMPRINTF(" load_addr: 0x%" PRIx32 "", image_info->load_addr);
169 DOMPRINTF(" load_end_addr: 0x%" PRIx32 "", image_info->load_end_addr);
170 DOMPRINTF(" bss_end_addr: 0x%" PRIx32 "", image_info->bss_end_addr);
171 DOMPRINTF(" entry_addr: 0x%" PRIx32 "", image_info->entry_addr);
172
173 /* Check the flags */
174 if ( (image_info->flags & FLAGS_MASK) != FLAGS_REQUIRED )
175 {
176 xc_dom_panic(dom->xch, XC_INVALID_KERNEL,
177 "%s: xen_bin_image_table flags required "
178 "0x%08" PRIx32 " found 0x%08" PRIx32 "",
179 __FUNCTION__, FLAGS_REQUIRED, image_info->flags & FLAGS_MASK);
180 return -EINVAL;
181 }
182
183 /* Sanity check on the addresses */
184 if ( (image_info->header_addr < image_info->load_addr) ||
185 ((char *) image_info - image) <
186 (image_info->header_addr - image_info->load_addr) )
187 {
188 xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: Invalid header_addr.",
189 __FUNCTION__);
190 return -EINVAL;
191 }
192
193 start_addr = image_info->header_addr - ((char *)image_info - image);
194 load_end_addr = image_info->load_end_addr ?: start_addr + image_size;
195 bss_end_addr = image_info->bss_end_addr ?: load_end_addr;
196
197 DOMPRINTF("%s: calculated addresses", __FUNCTION__);
198 DOMPRINTF(" start_addr: 0x%" PRIx32 "", start_addr);
199 DOMPRINTF(" load_end_addr: 0x%" PRIx32 "", load_end_addr);
200 DOMPRINTF(" bss_end_addr: 0x%" PRIx32 "", bss_end_addr);
201
202 if ( (start_addr + image_size) < load_end_addr )
203 {
204 xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: Invalid load_end_addr.",
205 __FUNCTION__);
206 return -EINVAL;
207 }
208
209 if ( bss_end_addr < load_end_addr)
210 {
211 xc_dom_panic(dom->xch, XC_INVALID_KERNEL, "%s: Invalid bss_end_addr.",
212 __FUNCTION__);
213 return -EINVAL;
214 }
215
216 dom->kernel_seg.vstart = image_info->load_addr;
217 dom->kernel_seg.vend = bss_end_addr;
218 dom->parms.virt_base = start_addr;
219 dom->parms.virt_entry = image_info->entry_addr;
220
221 pae_flags = image_info->flags & XEN_MULTIBOOT_FLAG_PAE_MASK;
222 switch (pae_flags >> XEN_MULTIBOOT_FLAG_PAE_SHIFT) {
223 case 0:
224 dom->guest_type = "xen-3.0-x86_32";
225 break;
226 case 1:
227 dom->guest_type = "xen-3.0-x86_32p";
228 break;
229 case 2:
230 dom->guest_type = "xen-3.0-x86_64";
231 break;
232 case 3:
233 /* Kernel detects PAE at runtime. So try to figure whenever
234 * xen supports PAE and advertise a PAE-capable kernel in case
235 * it does. */
236 dom->guest_type = "xen-3.0-x86_32";
237 if ( strstr(dom->xen_caps, "xen-3.0-x86_32p") )
238 {
239 DOMPRINTF("%s: PAE fixup", __FUNCTION__);
240 dom->guest_type = "xen-3.0-x86_32p";
241 dom->parms.pae = XEN_PAE_EXTCR3;
242 }
243 break;
244 }
245 return 0;
246 }
247
xc_dom_load_bin_kernel(struct xc_dom_image * dom)248 static int xc_dom_load_bin_kernel(struct xc_dom_image *dom)
249 {
250 struct xen_bin_image_table *image_info;
251 char *image = dom->kernel_blob;
252 char *dest;
253 size_t image_size = dom->kernel_size;
254 size_t dest_size;
255 uint32_t start_addr;
256 uint32_t load_end_addr;
257 uint32_t bss_end_addr;
258 uint32_t skip, text_size, bss_size;
259
260 image_info = find_table(dom);
261 if ( !image_info )
262 return -EINVAL;
263
264 start_addr = image_info->header_addr - ((char *)image_info - image);
265 load_end_addr = image_info->load_end_addr ?: start_addr + image_size;
266 bss_end_addr = image_info->bss_end_addr ?: load_end_addr;
267
268 /* It's possible that we need to skip the first part of the image */
269 skip = image_info->load_addr - start_addr;
270 text_size = load_end_addr - image_info->load_addr;
271 bss_size = bss_end_addr - load_end_addr;
272
273 DOMPRINTF("%s: calculated sizes", __FUNCTION__);
274 DOMPRINTF(" skip: 0x%" PRIx32 "", skip);
275 DOMPRINTF(" text_size: 0x%" PRIx32 "", text_size);
276 DOMPRINTF(" bss_size: 0x%" PRIx32 "", bss_size);
277
278 dest = xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart, &dest_size);
279 if ( dest == NULL )
280 {
281 DOMPRINTF("%s: xc_dom_vaddr_to_ptr(dom, dom->kernel_seg.vstart)"
282 " => NULL", __FUNCTION__);
283 return -EINVAL;
284 }
285
286 if ( dest_size < text_size ||
287 dest_size - text_size < bss_size )
288 {
289 DOMPRINTF("%s: mapped region is too small for image", __FUNCTION__);
290 return -EINVAL;
291 }
292
293 if ( image_size < skip ||
294 image_size - skip < text_size )
295 {
296 DOMPRINTF("%s: image is too small for declared text size",
297 __FUNCTION__);
298 return -EINVAL;
299 }
300
301 memcpy(dest, image + skip, text_size);
302 memset(dest + text_size, 0, bss_size);
303
304 return 0;
305 }
306
307 /* ------------------------------------------------------------------------ */
308
309 static struct xc_dom_loader bin_loader = {
310 .name = "multiboot-binary",
311 .probe = xc_dom_probe_bin_kernel,
312 .parser = xc_dom_parse_bin_kernel,
313 .loader = xc_dom_load_bin_kernel,
314 };
315
register_loader(void)316 static void __init register_loader(void)
317 {
318 xc_dom_register_loader(&bin_loader);
319 }
320
321 /*
322 * Local variables:
323 * mode: C
324 * c-file-style: "BSD"
325 * c-basic-offset: 4
326 * tab-width: 4
327 * indent-tabs-mode: nil
328 * End:
329 */
330