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