1 /*
2  * reloc.c
3  *
4  * 32-bit flat memory-map routines for relocating Multiboot structures
5  * and modules. This is most easily done early with paging disabled.
6  *
7  * Copyright (c) 2009, Citrix Systems, Inc.
8  * Copyright (c) 2013-2016 Oracle and/or its affiliates. All rights reserved.
9  *
10  * Authors:
11  *    Keir Fraser <keir@xen.org>
12  *    Daniel Kiper <daniel.kiper@oracle.com>
13  */
14 
15 /*
16  * This entry point is entered from xen/arch/x86/boot/head.S with:
17  *   - 0x04(%esp) = MAGIC,
18  *   - 0x08(%esp) = INFORMATION_ADDRESS,
19  *   - 0x0c(%esp) = TOPMOST_LOW_MEMORY_STACK_ADDRESS.
20  *   - 0x10(%esp) = BOOT_VIDEO_INFO_ADDRESS.
21  */
22 asm (
23     "    .text                         \n"
24     "    .globl _start                 \n"
25     "_start:                           \n"
26     "    jmp  reloc                    \n"
27     );
28 
29 #include "defs.h"
30 
31 #include <xen/kconfig.h>
32 #include <xen/multiboot.h>
33 #include <xen/multiboot2.h>
34 
35 #include <public/arch-x86/hvm/start_info.h>
36 
37 #ifdef CONFIG_VIDEO
38 # include "video.h"
39 
40 /* VESA control information */
41 struct __packed vesa_ctrl_info {
42     uint8_t signature[4];
43     uint16_t version;
44     uint32_t oem_name;
45     uint32_t capabilities;
46     uint32_t mode_list;
47     uint16_t mem_size;
48     /* We don't use any further fields. */
49 };
50 
51 /* VESA 2.0 mode information */
52 struct vesa_mode_info {
53     uint16_t attrib;
54     uint8_t window[14]; /* We don't use the individual fields. */
55     uint16_t bytes_per_line;
56     uint16_t width;
57     uint16_t height;
58     uint8_t cell_width;
59     uint8_t cell_height;
60     uint8_t nr_planes;
61     uint8_t depth;
62     uint8_t memory[5]; /* We don't use the individual fields. */
63     struct boot_video_colors colors;
64     uint8_t direct_color;
65     uint32_t base;
66     /* We don't use any further fields. */
67 };
68 #endif /* CONFIG_VIDEO */
69 
70 #define get_mb2_data(tag, type, member)   (((const multiboot2_tag_##type##_t *)(tag))->member)
71 #define get_mb2_string(tag, type, member) ((u32)get_mb2_data(tag, type, member))
72 
73 static u32 alloc;
74 
alloc_mem(u32 bytes)75 static u32 alloc_mem(u32 bytes)
76 {
77     return alloc -= ALIGN_UP(bytes, 16);
78 }
79 
zero_mem(u32 s,u32 bytes)80 static void zero_mem(u32 s, u32 bytes)
81 {
82     while ( bytes-- )
83         *(char *)s++ = 0;
84 }
85 
copy_mem(u32 src,u32 bytes)86 static u32 copy_mem(u32 src, u32 bytes)
87 {
88     u32 dst, dst_ret;
89 
90     dst = alloc_mem(bytes);
91     dst_ret = dst;
92 
93     while ( bytes-- )
94         *(char *)dst++ = *(char *)src++;
95 
96     return dst_ret;
97 }
98 
copy_string(u32 src)99 static u32 copy_string(u32 src)
100 {
101     u32 p;
102 
103     if ( !src )
104         return 0;
105 
106     for ( p = src; *(char *)p != '\0'; p++ )
107         continue;
108 
109     return copy_mem(src, p - src + 1);
110 }
111 
pvh_info_reloc(u32 in)112 static struct hvm_start_info *pvh_info_reloc(u32 in)
113 {
114     struct hvm_start_info *out;
115 
116     out = _p(copy_mem(in, sizeof(*out)));
117 
118     if ( out->cmdline_paddr )
119         out->cmdline_paddr = copy_string(out->cmdline_paddr);
120 
121     if ( out->nr_modules )
122     {
123         unsigned int i;
124         struct hvm_modlist_entry *mods;
125 
126         out->modlist_paddr =
127             copy_mem(out->modlist_paddr,
128                      out->nr_modules * sizeof(struct hvm_modlist_entry));
129 
130         mods = _p(out->modlist_paddr);
131 
132         for ( i = 0; i < out->nr_modules; i++ )
133         {
134             if ( mods[i].cmdline_paddr )
135                 mods[i].cmdline_paddr = copy_string(mods[i].cmdline_paddr);
136         }
137     }
138 
139     return out;
140 }
141 
mbi_reloc(u32 mbi_in)142 static multiboot_info_t *mbi_reloc(u32 mbi_in)
143 {
144     int i;
145     multiboot_info_t *mbi_out;
146 
147     mbi_out = _p(copy_mem(mbi_in, sizeof(*mbi_out)));
148 
149     if ( mbi_out->flags & MBI_CMDLINE )
150         mbi_out->cmdline = copy_string(mbi_out->cmdline);
151 
152     if ( mbi_out->flags & MBI_MODULES )
153     {
154         module_t *mods;
155 
156         mbi_out->mods_addr = copy_mem(mbi_out->mods_addr,
157                                       mbi_out->mods_count * sizeof(module_t));
158 
159         mods = _p(mbi_out->mods_addr);
160 
161         for ( i = 0; i < mbi_out->mods_count; i++ )
162         {
163             if ( mods[i].string )
164                 mods[i].string = copy_string(mods[i].string);
165         }
166     }
167 
168     if ( mbi_out->flags & MBI_MEMMAP )
169         mbi_out->mmap_addr = copy_mem(mbi_out->mmap_addr, mbi_out->mmap_length);
170 
171     if ( mbi_out->flags & MBI_LOADERNAME )
172         mbi_out->boot_loader_name = copy_string(mbi_out->boot_loader_name);
173 
174     /* Mask features we don't understand or don't relocate. */
175     mbi_out->flags &= (MBI_MEMLIMITS |
176                        MBI_CMDLINE |
177                        MBI_MODULES |
178                        MBI_MEMMAP |
179                        MBI_LOADERNAME);
180 
181     return mbi_out;
182 }
183 
mbi2_reloc(uint32_t mbi_in,uint32_t video_out)184 static multiboot_info_t *mbi2_reloc(uint32_t mbi_in, uint32_t video_out)
185 {
186     const multiboot2_fixed_t *mbi_fix = _p(mbi_in);
187     const multiboot2_memory_map_t *mmap_src;
188     const multiboot2_tag_t *tag;
189     module_t *mbi_out_mods = NULL;
190     memory_map_t *mmap_dst;
191     multiboot_info_t *mbi_out;
192 #ifdef CONFIG_VIDEO
193     struct boot_video_info *video = NULL;
194 #endif
195     u32 ptr;
196     unsigned int i, mod_idx = 0;
197 
198     ptr = alloc_mem(sizeof(*mbi_out));
199     mbi_out = _p(ptr);
200     zero_mem(ptr, sizeof(*mbi_out));
201 
202     /* Skip Multiboot2 information fixed part. */
203     ptr = ALIGN_UP(mbi_in + sizeof(*mbi_fix), MULTIBOOT2_TAG_ALIGN);
204 
205     /* Get the number of modules. */
206     for ( tag = _p(ptr); (u32)tag - mbi_in < mbi_fix->total_size;
207           tag = _p(ALIGN_UP((u32)tag + tag->size, MULTIBOOT2_TAG_ALIGN)) )
208     {
209         if ( tag->type == MULTIBOOT2_TAG_TYPE_MODULE )
210             ++mbi_out->mods_count;
211         else if ( tag->type == MULTIBOOT2_TAG_TYPE_END )
212             break;
213     }
214 
215     if ( mbi_out->mods_count )
216     {
217         mbi_out->flags |= MBI_MODULES;
218         /*
219          * We have to allocate one more module slot here. At some point
220          * __start_xen() may put Xen image placement into it.
221          */
222         mbi_out->mods_addr = alloc_mem((mbi_out->mods_count + 1) *
223                                        sizeof(*mbi_out_mods));
224         mbi_out_mods = _p(mbi_out->mods_addr);
225     }
226 
227     /* Skip Multiboot2 information fixed part. */
228     ptr = ALIGN_UP(mbi_in + sizeof(*mbi_fix), MULTIBOOT2_TAG_ALIGN);
229 
230     /* Put all needed data into mbi_out. */
231     for ( tag = _p(ptr); (u32)tag - mbi_in < mbi_fix->total_size;
232           tag = _p(ALIGN_UP((u32)tag + tag->size, MULTIBOOT2_TAG_ALIGN)) )
233     {
234         switch ( tag->type )
235         {
236         case MULTIBOOT2_TAG_TYPE_BOOT_LOADER_NAME:
237             mbi_out->flags |= MBI_LOADERNAME;
238             ptr = get_mb2_string(tag, string, string);
239             mbi_out->boot_loader_name = copy_string(ptr);
240             break;
241 
242         case MULTIBOOT2_TAG_TYPE_CMDLINE:
243             mbi_out->flags |= MBI_CMDLINE;
244             ptr = get_mb2_string(tag, string, string);
245             mbi_out->cmdline = copy_string(ptr);
246             break;
247 
248         case MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO:
249             mbi_out->flags |= MBI_MEMLIMITS;
250             mbi_out->mem_lower = get_mb2_data(tag, basic_meminfo, mem_lower);
251             mbi_out->mem_upper = get_mb2_data(tag, basic_meminfo, mem_upper);
252             break;
253 
254         case MULTIBOOT2_TAG_TYPE_MMAP:
255             if ( get_mb2_data(tag, mmap, entry_size) < sizeof(*mmap_src) )
256                 break;
257 
258             mbi_out->flags |= MBI_MEMMAP;
259             mbi_out->mmap_length = get_mb2_data(tag, mmap, size);
260             mbi_out->mmap_length -= sizeof(multiboot2_tag_mmap_t);
261             mbi_out->mmap_length /= get_mb2_data(tag, mmap, entry_size);
262             mbi_out->mmap_length *= sizeof(*mmap_dst);
263 
264             mbi_out->mmap_addr = alloc_mem(mbi_out->mmap_length);
265 
266             mmap_src = get_mb2_data(tag, mmap, entries);
267             mmap_dst = _p(mbi_out->mmap_addr);
268 
269             for ( i = 0; i < mbi_out->mmap_length / sizeof(*mmap_dst); i++ )
270             {
271                 /* Init size member properly. */
272                 mmap_dst[i].size = sizeof(*mmap_dst);
273                 mmap_dst[i].size -= sizeof(mmap_dst[i].size);
274                 /* Now copy a given region data. */
275                 mmap_dst[i].base_addr_low = (u32)mmap_src->addr;
276                 mmap_dst[i].base_addr_high = (u32)(mmap_src->addr >> 32);
277                 mmap_dst[i].length_low = (u32)mmap_src->len;
278                 mmap_dst[i].length_high = (u32)(mmap_src->len >> 32);
279                 mmap_dst[i].type = mmap_src->type;
280                 mmap_src = _p(mmap_src) + get_mb2_data(tag, mmap, entry_size);
281             }
282             break;
283 
284         case MULTIBOOT2_TAG_TYPE_MODULE:
285             if ( mod_idx >= mbi_out->mods_count )
286                 break;
287 
288             mbi_out_mods[mod_idx].mod_start = get_mb2_data(tag, module, mod_start);
289             mbi_out_mods[mod_idx].mod_end = get_mb2_data(tag, module, mod_end);
290             ptr = get_mb2_string(tag, module, cmdline);
291             mbi_out_mods[mod_idx].string = copy_string(ptr);
292             mbi_out_mods[mod_idx].reserved = 0;
293             ++mod_idx;
294             break;
295 
296 #ifdef CONFIG_VIDEO
297         case MULTIBOOT2_TAG_TYPE_VBE:
298             if ( video_out )
299             {
300                 const struct vesa_ctrl_info *ci;
301                 const struct vesa_mode_info *mi;
302 
303                 video = _p(video_out);
304                 ci = (const void *)get_mb2_data(tag, vbe, vbe_control_info);
305                 mi = (const void *)get_mb2_data(tag, vbe, vbe_mode_info);
306 
307                 if ( ci->version >= 0x0200 && (mi->attrib & 0x9b) == 0x9b )
308                 {
309                     video->capabilities = ci->capabilities;
310                     video->lfb_linelength = mi->bytes_per_line;
311                     video->lfb_width = mi->width;
312                     video->lfb_height = mi->height;
313                     video->lfb_depth = mi->depth;
314                     video->lfb_base = mi->base;
315                     video->lfb_size = ci->mem_size;
316                     video->colors = mi->colors;
317                     video->vesa_attrib = mi->attrib;
318                 }
319 
320                 video->vesapm.seg = get_mb2_data(tag, vbe, vbe_interface_seg);
321                 video->vesapm.off = get_mb2_data(tag, vbe, vbe_interface_off);
322             }
323             break;
324 
325         case MULTIBOOT2_TAG_TYPE_FRAMEBUFFER:
326             if ( (get_mb2_data(tag, framebuffer, framebuffer_type) !=
327                   MULTIBOOT2_FRAMEBUFFER_TYPE_RGB) )
328             {
329                 video_out = 0;
330                 video = NULL;
331             }
332             break;
333 #endif /* CONFIG_VIDEO */
334 
335         case MULTIBOOT2_TAG_TYPE_END:
336             goto end;
337 
338         default:
339             break;
340         }
341     }
342 
343  end:
344 
345 #ifdef CONFIG_VIDEO
346     if ( video )
347         video->orig_video_isVGA = 0x23;
348 #endif
349 
350     return mbi_out;
351 }
352 
353 /* SAF-1-safe */
reloc(uint32_t magic,uint32_t in,uint32_t trampoline,uint32_t video_info)354 void *__stdcall reloc(uint32_t magic, uint32_t in, uint32_t trampoline,
355                       uint32_t video_info)
356 {
357     alloc = trampoline;
358 
359     switch ( magic )
360     {
361     case MULTIBOOT_BOOTLOADER_MAGIC:
362         return mbi_reloc(in);
363 
364     case MULTIBOOT2_BOOTLOADER_MAGIC:
365         return mbi2_reloc(in, video_info);
366 
367     case XEN_HVM_START_MAGIC_VALUE:
368         if ( IS_ENABLED(CONFIG_PVH_GUEST) )
369             return pvh_info_reloc(in);
370         /* Fallthrough */
371 
372     default:
373         /* Nothing we can do */
374         return NULL;
375     }
376 }
377 
378 /*
379  * Local variables:
380  * mode: C
381  * c-file-style: "BSD"
382  * c-basic-offset: 4
383  * tab-width: 4
384  * indent-tabs-mode: nil
385  * End:
386  */
387