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  *   - 0x4(%esp) = MAGIC,
18  *   - 0x8(%esp) = INFORMATION_ADDRESS,
19  *   - 0xc(%esp) = TOPMOST_LOW_MEMORY_STACK_ADDRESS.
20  */
21 asm (
22     "    .text                         \n"
23     "    .globl _start                 \n"
24     "_start:                           \n"
25     "    jmp  reloc                    \n"
26     );
27 
28 #include "defs.h"
29 #include "../../../include/xen/multiboot.h"
30 #include "../../../include/xen/multiboot2.h"
31 
32 #include "../../../include/xen/kconfig.h"
33 #include <public/arch-x86/hvm/start_info.h>
34 
35 #define get_mb2_data(tag, type, member)   (((multiboot2_tag_##type##_t *)(tag))->member)
36 #define get_mb2_string(tag, type, member) ((u32)get_mb2_data(tag, type, member))
37 
38 static u32 alloc;
39 
alloc_mem(u32 bytes)40 static u32 alloc_mem(u32 bytes)
41 {
42     return alloc -= ALIGN_UP(bytes, 16);
43 }
44 
zero_mem(u32 s,u32 bytes)45 static void zero_mem(u32 s, u32 bytes)
46 {
47     while ( bytes-- )
48         *(char *)s++ = 0;
49 }
50 
copy_mem(u32 src,u32 bytes)51 static u32 copy_mem(u32 src, u32 bytes)
52 {
53     u32 dst, dst_ret;
54 
55     dst = alloc_mem(bytes);
56     dst_ret = dst;
57 
58     while ( bytes-- )
59         *(char *)dst++ = *(char *)src++;
60 
61     return dst_ret;
62 }
63 
copy_string(u32 src)64 static u32 copy_string(u32 src)
65 {
66     u32 p;
67 
68     if ( !src )
69         return 0;
70 
71     for ( p = src; *(char *)p != '\0'; p++ )
72         continue;
73 
74     return copy_mem(src, p - src + 1);
75 }
76 
pvh_info_reloc(u32 in)77 static struct hvm_start_info *pvh_info_reloc(u32 in)
78 {
79     struct hvm_start_info *out;
80 
81     out = _p(copy_mem(in, sizeof(*out)));
82 
83     if ( out->cmdline_paddr )
84         out->cmdline_paddr = copy_string(out->cmdline_paddr);
85 
86     if ( out->nr_modules )
87     {
88         unsigned int i;
89         struct hvm_modlist_entry *mods;
90 
91         out->modlist_paddr =
92             copy_mem(out->modlist_paddr,
93                      out->nr_modules * sizeof(struct hvm_modlist_entry));
94 
95         mods = _p(out->modlist_paddr);
96 
97         for ( i = 0; i < out->nr_modules; i++ )
98         {
99             if ( mods[i].cmdline_paddr )
100                 mods[i].cmdline_paddr = copy_string(mods[i].cmdline_paddr);
101         }
102     }
103 
104     return out;
105 }
106 
mbi_reloc(u32 mbi_in)107 static multiboot_info_t *mbi_reloc(u32 mbi_in)
108 {
109     int i;
110     multiboot_info_t *mbi_out;
111 
112     mbi_out = _p(copy_mem(mbi_in, sizeof(*mbi_out)));
113 
114     if ( mbi_out->flags & MBI_CMDLINE )
115         mbi_out->cmdline = copy_string(mbi_out->cmdline);
116 
117     if ( mbi_out->flags & MBI_MODULES )
118     {
119         module_t *mods;
120 
121         mbi_out->mods_addr = copy_mem(mbi_out->mods_addr,
122                                       mbi_out->mods_count * sizeof(module_t));
123 
124         mods = _p(mbi_out->mods_addr);
125 
126         for ( i = 0; i < mbi_out->mods_count; i++ )
127         {
128             if ( mods[i].string )
129                 mods[i].string = copy_string(mods[i].string);
130         }
131     }
132 
133     if ( mbi_out->flags & MBI_MEMMAP )
134         mbi_out->mmap_addr = copy_mem(mbi_out->mmap_addr, mbi_out->mmap_length);
135 
136     if ( mbi_out->flags & MBI_LOADERNAME )
137         mbi_out->boot_loader_name = copy_string(mbi_out->boot_loader_name);
138 
139     /* Mask features we don't understand or don't relocate. */
140     mbi_out->flags &= (MBI_MEMLIMITS |
141                        MBI_CMDLINE |
142                        MBI_MODULES |
143                        MBI_MEMMAP |
144                        MBI_LOADERNAME);
145 
146     return mbi_out;
147 }
148 
mbi2_reloc(u32 mbi_in)149 static multiboot_info_t *mbi2_reloc(u32 mbi_in)
150 {
151     const multiboot2_fixed_t *mbi_fix = _p(mbi_in);
152     const multiboot2_memory_map_t *mmap_src;
153     const multiboot2_tag_t *tag;
154     module_t *mbi_out_mods = NULL;
155     memory_map_t *mmap_dst;
156     multiboot_info_t *mbi_out;
157     u32 ptr;
158     unsigned int i, mod_idx = 0;
159 
160     ptr = alloc_mem(sizeof(*mbi_out));
161     mbi_out = _p(ptr);
162     zero_mem(ptr, sizeof(*mbi_out));
163 
164     /* Skip Multiboot2 information fixed part. */
165     ptr = ALIGN_UP(mbi_in + sizeof(*mbi_fix), MULTIBOOT2_TAG_ALIGN);
166 
167     /* Get the number of modules. */
168     for ( tag = _p(ptr); (u32)tag - mbi_in < mbi_fix->total_size;
169           tag = _p(ALIGN_UP((u32)tag + tag->size, MULTIBOOT2_TAG_ALIGN)) )
170     {
171         if ( tag->type == MULTIBOOT2_TAG_TYPE_MODULE )
172             ++mbi_out->mods_count;
173         else if ( tag->type == MULTIBOOT2_TAG_TYPE_END )
174             break;
175     }
176 
177     if ( mbi_out->mods_count )
178     {
179         mbi_out->flags |= MBI_MODULES;
180         /*
181          * We have to allocate one more module slot here. At some point
182          * __start_xen() may put Xen image placement into it.
183          */
184         mbi_out->mods_addr = alloc_mem((mbi_out->mods_count + 1) *
185                                        sizeof(*mbi_out_mods));
186         mbi_out_mods = _p(mbi_out->mods_addr);
187     }
188 
189     /* Skip Multiboot2 information fixed part. */
190     ptr = ALIGN_UP(mbi_in + sizeof(*mbi_fix), MULTIBOOT2_TAG_ALIGN);
191 
192     /* Put all needed data into mbi_out. */
193     for ( tag = _p(ptr); (u32)tag - mbi_in < mbi_fix->total_size;
194           tag = _p(ALIGN_UP((u32)tag + tag->size, MULTIBOOT2_TAG_ALIGN)) )
195         switch ( tag->type )
196         {
197         case MULTIBOOT2_TAG_TYPE_BOOT_LOADER_NAME:
198             mbi_out->flags |= MBI_LOADERNAME;
199             ptr = get_mb2_string(tag, string, string);
200             mbi_out->boot_loader_name = copy_string(ptr);
201             break;
202 
203         case MULTIBOOT2_TAG_TYPE_CMDLINE:
204             mbi_out->flags |= MBI_CMDLINE;
205             ptr = get_mb2_string(tag, string, string);
206             mbi_out->cmdline = copy_string(ptr);
207             break;
208 
209         case MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO:
210             mbi_out->flags |= MBI_MEMLIMITS;
211             mbi_out->mem_lower = get_mb2_data(tag, basic_meminfo, mem_lower);
212             mbi_out->mem_upper = get_mb2_data(tag, basic_meminfo, mem_upper);
213             break;
214 
215         case MULTIBOOT2_TAG_TYPE_MMAP:
216             if ( get_mb2_data(tag, mmap, entry_size) < sizeof(*mmap_src) )
217                 break;
218 
219             mbi_out->flags |= MBI_MEMMAP;
220             mbi_out->mmap_length = get_mb2_data(tag, mmap, size);
221             mbi_out->mmap_length -= sizeof(multiboot2_tag_mmap_t);
222             mbi_out->mmap_length /= get_mb2_data(tag, mmap, entry_size);
223             mbi_out->mmap_length *= sizeof(*mmap_dst);
224 
225             mbi_out->mmap_addr = alloc_mem(mbi_out->mmap_length);
226 
227             mmap_src = get_mb2_data(tag, mmap, entries);
228             mmap_dst = _p(mbi_out->mmap_addr);
229 
230             for ( i = 0; i < mbi_out->mmap_length / sizeof(*mmap_dst); i++ )
231             {
232                 /* Init size member properly. */
233                 mmap_dst[i].size = sizeof(*mmap_dst);
234                 mmap_dst[i].size -= sizeof(mmap_dst[i].size);
235                 /* Now copy a given region data. */
236                 mmap_dst[i].base_addr_low = (u32)mmap_src->addr;
237                 mmap_dst[i].base_addr_high = (u32)(mmap_src->addr >> 32);
238                 mmap_dst[i].length_low = (u32)mmap_src->len;
239                 mmap_dst[i].length_high = (u32)(mmap_src->len >> 32);
240                 mmap_dst[i].type = mmap_src->type;
241                 mmap_src = _p(mmap_src) + get_mb2_data(tag, mmap, entry_size);
242             }
243             break;
244 
245         case MULTIBOOT2_TAG_TYPE_MODULE:
246             if ( mod_idx >= mbi_out->mods_count )
247                 break;
248 
249             mbi_out_mods[mod_idx].mod_start = get_mb2_data(tag, module, mod_start);
250             mbi_out_mods[mod_idx].mod_end = get_mb2_data(tag, module, mod_end);
251             ptr = get_mb2_string(tag, module, cmdline);
252             mbi_out_mods[mod_idx].string = copy_string(ptr);
253             mbi_out_mods[mod_idx].reserved = 0;
254             ++mod_idx;
255             break;
256 
257         case MULTIBOOT2_TAG_TYPE_END:
258             return mbi_out;
259 
260         default:
261             break;
262         }
263 
264     return mbi_out;
265 }
266 
reloc(u32 magic,u32 in,u32 trampoline)267 void * __stdcall reloc(u32 magic, u32 in, u32 trampoline)
268 {
269     alloc = trampoline;
270 
271     switch ( magic )
272     {
273     case MULTIBOOT_BOOTLOADER_MAGIC:
274         return mbi_reloc(in);
275 
276     case MULTIBOOT2_BOOTLOADER_MAGIC:
277         return mbi2_reloc(in);
278 
279     case XEN_HVM_START_MAGIC_VALUE:
280         if ( IS_ENABLED(CONFIG_PVH_GUEST) )
281             return pvh_info_reloc(in);
282         /* Fallthrough */
283 
284     default:
285         /* Nothing we can do */
286         return NULL;
287     }
288 }
289 
290 /*
291  * Local variables:
292  * mode: C
293  * c-file-style: "BSD"
294  * c-basic-offset: 4
295  * tab-width: 4
296  * indent-tabs-mode: nil
297  * End:
298  */
299