1 /*
2 * Xen domain builder -- xen booter.
3 *
4 * This is the code which actually boots a fresh
5 * prepared domain image as xen guest domain.
6 *
7 * ==> this is the only domain builder code piece
8 * where xen hypercalls are allowed <==
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation;
13 * version 2.1 of the License.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
22 *
23 * written 2006 by Gerd Hoffmann <kraxel@suse.de>.
24 *
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <inttypes.h>
31 #include <zlib.h>
32
33 #include "xg_private.h"
34 #include "xc_dom.h"
35 #include "xc_core.h"
36 #include <xen/hvm/params.h>
37 #include <xen/grant_table.h>
38
39 /* ------------------------------------------------------------------------ */
40
setup_hypercall_page(struct xc_dom_image * dom)41 static int setup_hypercall_page(struct xc_dom_image *dom)
42 {
43 DECLARE_DOMCTL;
44 xen_pfn_t pfn;
45 int rc;
46
47 if ( dom->parms.virt_hypercall == -1 )
48 return 0;
49 pfn = (dom->parms.virt_hypercall - dom->parms.virt_base)
50 >> XC_DOM_PAGE_SHIFT(dom);
51
52 DOMPRINTF("%s: vaddr=0x%" PRIx64 " pfn=0x%" PRIpfn "", __FUNCTION__,
53 dom->parms.virt_hypercall, pfn);
54 domctl.cmd = XEN_DOMCTL_hypercall_init;
55 domctl.domain = dom->guest_domid;
56 domctl.u.hypercall_init.gmfn = xc_dom_p2m(dom, pfn);
57 rc = do_domctl(dom->xch, &domctl);
58 if ( rc != 0 )
59 xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
60 "%s: HYPERCALL_INIT failed: %d - %s)",
61 __FUNCTION__, errno, strerror(errno));
62 return rc;
63 }
64
65
66 /* ------------------------------------------------------------------------ */
67
xc_dom_compat_check(struct xc_dom_image * dom)68 int xc_dom_compat_check(struct xc_dom_image *dom)
69 {
70 xen_capabilities_info_t xen_caps;
71 char *item, *ptr;
72 int match, found = 0;
73
74 strncpy(xen_caps, dom->xen_caps, XEN_CAPABILITIES_INFO_LEN - 1);
75 xen_caps[XEN_CAPABILITIES_INFO_LEN - 1] = '\0';
76
77 for ( item = strtok_r(xen_caps, " ", &ptr);
78 item != NULL ; item = strtok_r(NULL, " ", &ptr) )
79 {
80 match = !strcmp(dom->guest_type, item);
81 DOMPRINTF("%s: supported guest type: %s%s", __FUNCTION__,
82 item, match ? " <= matches" : "");
83 if ( match )
84 found++;
85 }
86 if ( !found )
87 xc_dom_panic(dom->xch, XC_INVALID_KERNEL,
88 "%s: guest type %s not supported by xen kernel, sorry",
89 __FUNCTION__, dom->guest_type);
90
91 return found;
92 }
93
xc_dom_boot_xen_init(struct xc_dom_image * dom,xc_interface * xch,uint32_t domid)94 int xc_dom_boot_xen_init(struct xc_dom_image *dom, xc_interface *xch, uint32_t domid)
95 {
96 dom->xch = xch;
97 dom->guest_domid = domid;
98
99 dom->xen_version = xc_version(xch, XENVER_version, NULL);
100 if ( xc_version(xch, XENVER_capabilities, &dom->xen_caps) < 0 )
101 {
102 xc_dom_panic(xch, XC_INTERNAL_ERROR, "can't get xen capabilities");
103 return -1;
104 }
105 DOMPRINTF("%s: ver %d.%d, caps %s", __FUNCTION__,
106 dom->xen_version >> 16, dom->xen_version & 0xff,
107 dom->xen_caps);
108 return 0;
109 }
110
xc_dom_boot_mem_init(struct xc_dom_image * dom)111 int xc_dom_boot_mem_init(struct xc_dom_image *dom)
112 {
113 long rc;
114
115 DOMPRINTF_CALLED(dom->xch);
116
117 rc = dom->arch_hooks->meminit(dom);
118 if ( rc != 0 )
119 {
120 xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY,
121 "%s: can't allocate low memory for domain",
122 __FUNCTION__);
123 return rc;
124 }
125
126 return 0;
127 }
128
xc_dom_boot_domU_map(struct xc_dom_image * dom,xen_pfn_t pfn,xen_pfn_t count)129 void *xc_dom_boot_domU_map(struct xc_dom_image *dom, xen_pfn_t pfn,
130 xen_pfn_t count)
131 {
132 int page_shift = XC_DOM_PAGE_SHIFT(dom);
133 privcmd_mmap_entry_t *entries;
134 void *ptr;
135 int i;
136 int err;
137
138 entries = xc_dom_malloc(dom, count * sizeof(privcmd_mmap_entry_t));
139 if ( entries == NULL )
140 {
141 xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
142 "%s: failed to mmap domU pages 0x%" PRIpfn "+0x%" PRIpfn
143 " [malloc]", __FUNCTION__, pfn, count);
144 return NULL;
145 }
146
147 for ( i = 0; i < count; i++ )
148 entries[i].mfn = xc_dom_p2m(dom, pfn + i);
149
150 ptr = xc_map_foreign_ranges(dom->xch, dom->guest_domid,
151 count << page_shift, PROT_READ | PROT_WRITE, 1 << page_shift,
152 entries, count);
153 if ( ptr == NULL )
154 {
155 err = errno;
156 xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
157 "%s: failed to mmap domU pages 0x%" PRIpfn "+0x%" PRIpfn
158 " [mmap, errno=%i (%s)]", __FUNCTION__, pfn, count,
159 err, strerror(err));
160 return NULL;
161 }
162
163 return ptr;
164 }
165
xc_dom_boot_image(struct xc_dom_image * dom)166 int xc_dom_boot_image(struct xc_dom_image *dom)
167 {
168 xc_dominfo_t info;
169 int rc;
170
171 DOMPRINTF_CALLED(dom->xch);
172
173 /* misc stuff*/
174 if ( (rc = dom->arch_hooks->bootearly(dom)) != 0 )
175 return rc;
176
177 /* collect some info */
178 rc = xc_domain_getinfo(dom->xch, dom->guest_domid, 1, &info);
179 if ( rc < 0 )
180 {
181 xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
182 "%s: getdomaininfo failed (rc=%d)", __FUNCTION__, rc);
183 return rc;
184 }
185 if ( rc == 0 || info.domid != dom->guest_domid )
186 {
187 xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
188 "%s: Huh? No domains found (nr_domains=%d) "
189 "or domid mismatch (%d != %d)", __FUNCTION__,
190 rc, info.domid, dom->guest_domid);
191 return -1;
192 }
193 dom->shared_info_mfn = info.shared_info_frame;
194
195 /* sanity checks */
196 if ( !xc_dom_compat_check(dom) )
197 return -1;
198
199 /* initial mm setup */
200 if ( dom->arch_hooks->setup_pgtables &&
201 (rc = dom->arch_hooks->setup_pgtables(dom)) != 0 )
202 return rc;
203
204 /* start info page */
205 if ( dom->arch_hooks->start_info )
206 dom->arch_hooks->start_info(dom);
207
208 /* hypercall page */
209 if ( (rc = setup_hypercall_page(dom)) != 0 )
210 return rc;
211 xc_dom_log_memory_footprint(dom);
212
213 /* misc x86 stuff */
214 if ( (rc = dom->arch_hooks->bootlate(dom)) != 0 )
215 return rc;
216
217 /* let the vm run */
218 if ( (rc = dom->arch_hooks->vcpu(dom)) != 0 )
219 return rc;
220 xc_dom_unmap_all(dom);
221
222 return rc;
223 }
224
xc_dom_gnttab_setup(xc_interface * xch,uint32_t domid)225 static xen_pfn_t xc_dom_gnttab_setup(xc_interface *xch, uint32_t domid)
226 {
227 gnttab_setup_table_t setup;
228 DECLARE_HYPERCALL_BUFFER(xen_pfn_t, gmfnp);
229 int rc;
230 xen_pfn_t gmfn;
231
232 gmfnp = xc_hypercall_buffer_alloc(xch, gmfnp, sizeof(*gmfnp));
233 if (gmfnp == NULL)
234 return -1;
235
236 setup.dom = domid;
237 setup.nr_frames = 1;
238 set_xen_guest_handle(setup.frame_list, gmfnp);
239 setup.status = 0;
240
241 rc = xc_gnttab_op(xch, GNTTABOP_setup_table, &setup, sizeof(setup), 1);
242 gmfn = *gmfnp;
243 xc_hypercall_buffer_free(xch, gmfnp);
244
245 if ( rc != 0 || setup.status != GNTST_okay )
246 {
247 xc_dom_panic(xch, XC_INTERNAL_ERROR,
248 "%s: failed to setup domU grant table "
249 "[errno=%d, status=%" PRId16 "]\n",
250 __FUNCTION__, rc != 0 ? errno : 0, setup.status);
251 return -1;
252 }
253
254 return gmfn;
255 }
256
xc_dom_set_gnttab_entry(xc_interface * xch,grant_entry_v1_t * gnttab,unsigned int idx,uint32_t guest_domid,uint32_t backend_domid,xen_pfn_t guest_gfn)257 static void xc_dom_set_gnttab_entry(xc_interface *xch,
258 grant_entry_v1_t *gnttab,
259 unsigned int idx,
260 uint32_t guest_domid,
261 uint32_t backend_domid,
262 xen_pfn_t guest_gfn)
263 {
264 if ( guest_domid == backend_domid || guest_gfn == -1 )
265 return;
266
267 xc_dom_printf(xch, "%s: d%d gnt[%u] -> d%d 0x%"PRI_xen_pfn,
268 __func__, guest_domid, idx, backend_domid, guest_gfn);
269
270 gnttab[idx].flags = GTF_permit_access;
271 gnttab[idx].domid = backend_domid;
272 gnttab[idx].frame = guest_gfn;
273 }
274
compat_gnttab_seed(xc_interface * xch,uint32_t domid,xen_pfn_t console_gfn,xen_pfn_t xenstore_gfn,uint32_t console_domid,uint32_t xenstore_domid)275 static int compat_gnttab_seed(xc_interface *xch, uint32_t domid,
276 xen_pfn_t console_gfn,
277 xen_pfn_t xenstore_gfn,
278 uint32_t console_domid,
279 uint32_t xenstore_domid)
280 {
281
282 xen_pfn_t gnttab_gfn;
283 grant_entry_v1_t *gnttab;
284
285 gnttab_gfn = xc_dom_gnttab_setup(xch, domid);
286 if ( gnttab_gfn == -1 )
287 return -1;
288
289 gnttab = xc_map_foreign_range(xch,
290 domid,
291 PAGE_SIZE,
292 PROT_READ|PROT_WRITE,
293 gnttab_gfn);
294 if ( gnttab == NULL )
295 {
296 xc_dom_panic(xch, XC_INTERNAL_ERROR,
297 "%s: failed to map d%d grant table "
298 "[errno=%d]\n",
299 __func__, domid, errno);
300 return -1;
301 }
302
303 xc_dom_set_gnttab_entry(xch, gnttab, GNTTAB_RESERVED_CONSOLE,
304 domid, console_domid, console_gfn);
305 xc_dom_set_gnttab_entry(xch, gnttab, GNTTAB_RESERVED_XENSTORE,
306 domid, xenstore_domid, xenstore_gfn);
307
308 if ( munmap(gnttab, PAGE_SIZE) == -1 )
309 {
310 xc_dom_panic(xch, XC_INTERNAL_ERROR,
311 "%s: failed to unmap d%d grant table "
312 "[errno=%d]\n",
313 __func__, domid, errno);
314 return -1;
315 }
316
317 /* Guest shouldn't really touch its grant table until it has
318 * enabled its caches. But lets be nice. */
319 xc_domain_cacheflush(xch, domid, gnttab_gfn, 1);
320
321 return 0;
322 }
323
compat_gnttab_hvm_seed(xc_interface * xch,uint32_t domid,xen_pfn_t console_gfn,xen_pfn_t xenstore_gfn,uint32_t console_domid,uint32_t xenstore_domid)324 static int compat_gnttab_hvm_seed(xc_interface *xch, uint32_t domid,
325 xen_pfn_t console_gfn,
326 xen_pfn_t xenstore_gfn,
327 uint32_t console_domid,
328 uint32_t xenstore_domid)
329 {
330 int rc;
331 xen_pfn_t scratch_gfn;
332 struct xen_add_to_physmap xatp = {
333 .domid = domid,
334 .space = XENMAPSPACE_grant_table,
335 .idx = 0,
336 };
337 struct xen_remove_from_physmap xrfp = {
338 .domid = domid,
339 };
340
341 rc = xc_core_arch_get_scratch_gpfn(xch, domid, &scratch_gfn);
342 if ( rc < 0 )
343 {
344 xc_dom_panic(xch, XC_INTERNAL_ERROR,
345 "%s: failed to get a scratch gfn from d%d"
346 "[errno=%d]\n",
347 __func__, domid, errno);
348 return -1;
349 }
350 xatp.gpfn = scratch_gfn;
351 xrfp.gpfn = scratch_gfn;
352
353 xc_dom_printf(xch, "%s: d%d: pfn=0x%"PRI_xen_pfn, __func__,
354 domid, scratch_gfn);
355
356 rc = do_memory_op(xch, XENMEM_add_to_physmap, &xatp, sizeof(xatp));
357 if ( rc != 0 )
358 {
359 xc_dom_panic(xch, XC_INTERNAL_ERROR,
360 "%s: failed to add gnttab to d%d physmap "
361 "[errno=%d]\n",
362 __func__, domid, errno);
363 return -1;
364 }
365
366 rc = compat_gnttab_seed(xch, domid,
367 console_gfn, xenstore_gfn,
368 console_domid, xenstore_domid);
369 if (rc != 0)
370 {
371 xc_dom_panic(xch, XC_INTERNAL_ERROR,
372 "%s: failed to seed gnttab entries for d%d\n",
373 __func__, domid);
374 (void) do_memory_op(xch, XENMEM_remove_from_physmap, &xrfp,
375 sizeof(xrfp));
376 return -1;
377 }
378
379 rc = do_memory_op(xch, XENMEM_remove_from_physmap, &xrfp, sizeof(xrfp));
380 if (rc != 0)
381 {
382 xc_dom_panic(xch, XC_INTERNAL_ERROR,
383 "%s: failed to remove gnttab from d%d physmap "
384 "[errno=%d]\n",
385 __func__, domid, errno);
386 return -1;
387 }
388
389 return 0;
390 }
391
xc_dom_gnttab_seed(xc_interface * xch,uint32_t guest_domid,bool is_hvm,xen_pfn_t console_gfn,xen_pfn_t xenstore_gfn,uint32_t console_domid,uint32_t xenstore_domid)392 int xc_dom_gnttab_seed(xc_interface *xch, uint32_t guest_domid,
393 bool is_hvm, xen_pfn_t console_gfn,
394 xen_pfn_t xenstore_gfn, uint32_t console_domid,
395 uint32_t xenstore_domid)
396 {
397 xenforeignmemory_handle* fmem = xch->fmem;
398 xenforeignmemory_resource_handle *fres;
399 void *addr = NULL;
400
401 fres = xenforeignmemory_map_resource(
402 fmem, guest_domid, XENMEM_resource_grant_table,
403 XENMEM_resource_grant_table_id_shared, 0, 1, &addr,
404 PROT_READ | PROT_WRITE, 0);
405 if ( !fres )
406 {
407 if ( errno == EOPNOTSUPP )
408 return is_hvm ?
409 compat_gnttab_hvm_seed(xch, guest_domid,
410 console_gfn, xenstore_gfn,
411 console_domid, xenstore_domid) :
412 compat_gnttab_seed(xch, guest_domid,
413 console_gfn, xenstore_gfn,
414 console_domid, xenstore_domid);
415
416 xc_dom_panic(xch, XC_INTERNAL_ERROR,
417 "%s: failed to acquire d%d grant table [errno=%d]\n",
418 __func__, guest_domid, errno);
419 return -1;
420 }
421
422 xc_dom_set_gnttab_entry(xch, addr, GNTTAB_RESERVED_CONSOLE,
423 guest_domid, console_domid, console_gfn);
424 xc_dom_set_gnttab_entry(xch, addr, GNTTAB_RESERVED_XENSTORE,
425 guest_domid, xenstore_domid, xenstore_gfn);
426
427 xenforeignmemory_unmap_resource(fmem, fres);
428
429 return 0;
430 }
431
xc_dom_gnttab_init(struct xc_dom_image * dom)432 int xc_dom_gnttab_init(struct xc_dom_image *dom)
433 {
434 bool is_hvm = xc_dom_translated(dom);
435 xen_pfn_t console_gfn = xc_dom_p2m(dom, dom->console_pfn);
436 xen_pfn_t xenstore_gfn = xc_dom_p2m(dom, dom->xenstore_pfn);
437
438 return xc_dom_gnttab_seed(dom->xch, dom->guest_domid, is_hvm,
439 console_gfn, xenstore_gfn,
440 dom->console_domid, dom->xenstore_domid);
441 }
442
443 /*
444 * Local variables:
445 * mode: C
446 * c-file-style: "BSD"
447 * c-basic-offset: 4
448 * tab-width: 4
449 * indent-tabs-mode: nil
450 * End:
451 */
452