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