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 ( (rc = xc_dom_update_guest_p2m(dom)) != 0 )
201 return rc;
202 if ( dom->arch_hooks->setup_pgtables )
203 if ( (rc = dom->arch_hooks->setup_pgtables(dom)) != 0 )
204 return rc;
205
206 /* start info page */
207 if ( dom->arch_hooks->start_info )
208 dom->arch_hooks->start_info(dom);
209
210 /* hypercall page */
211 if ( (rc = setup_hypercall_page(dom)) != 0 )
212 return rc;
213 xc_dom_log_memory_footprint(dom);
214
215 /* misc x86 stuff */
216 if ( (rc = dom->arch_hooks->bootlate(dom)) != 0 )
217 return rc;
218
219 /* let the vm run */
220 if ( (rc = dom->arch_hooks->vcpu(dom)) != 0 )
221 return rc;
222 xc_dom_unmap_all(dom);
223
224 return rc;
225 }
226
xc_dom_gnttab_setup(xc_interface * xch,uint32_t domid)227 static xen_pfn_t xc_dom_gnttab_setup(xc_interface *xch, uint32_t domid)
228 {
229 gnttab_setup_table_t setup;
230 DECLARE_HYPERCALL_BUFFER(xen_pfn_t, gmfnp);
231 int rc;
232 xen_pfn_t gmfn;
233
234 gmfnp = xc_hypercall_buffer_alloc(xch, gmfnp, sizeof(*gmfnp));
235 if (gmfnp == NULL)
236 return -1;
237
238 setup.dom = domid;
239 setup.nr_frames = 1;
240 set_xen_guest_handle(setup.frame_list, gmfnp);
241 setup.status = 0;
242
243 rc = xc_gnttab_op(xch, GNTTABOP_setup_table, &setup, sizeof(setup), 1);
244 gmfn = *gmfnp;
245 xc_hypercall_buffer_free(xch, gmfnp);
246
247 if ( rc != 0 || setup.status != GNTST_okay )
248 {
249 xc_dom_panic(xch, XC_INTERNAL_ERROR,
250 "%s: failed to setup domU grant table "
251 "[errno=%d, status=%" PRId16 "]\n",
252 __FUNCTION__, rc != 0 ? errno : 0, setup.status);
253 return -1;
254 }
255
256 return gmfn;
257 }
258
xc_dom_gnttab_seed(xc_interface * xch,uint32_t domid,xen_pfn_t console_gmfn,xen_pfn_t xenstore_gmfn,uint32_t console_domid,uint32_t xenstore_domid)259 int xc_dom_gnttab_seed(xc_interface *xch, uint32_t domid,
260 xen_pfn_t console_gmfn,
261 xen_pfn_t xenstore_gmfn,
262 uint32_t console_domid,
263 uint32_t xenstore_domid)
264 {
265
266 xen_pfn_t gnttab_gmfn;
267 grant_entry_v1_t *gnttab;
268
269 gnttab_gmfn = xc_dom_gnttab_setup(xch, domid);
270 if ( gnttab_gmfn == -1 )
271 return -1;
272
273 gnttab = xc_map_foreign_range(xch,
274 domid,
275 PAGE_SIZE,
276 PROT_READ|PROT_WRITE,
277 gnttab_gmfn);
278 if ( gnttab == NULL )
279 {
280 xc_dom_panic(xch, XC_INTERNAL_ERROR,
281 "%s: failed to map domU grant table "
282 "[errno=%d]\n",
283 __FUNCTION__, errno);
284 return -1;
285 }
286
287 if ( domid != console_domid && console_gmfn != -1)
288 {
289 gnttab[GNTTAB_RESERVED_CONSOLE].flags = GTF_permit_access;
290 gnttab[GNTTAB_RESERVED_CONSOLE].domid = console_domid;
291 gnttab[GNTTAB_RESERVED_CONSOLE].frame = console_gmfn;
292 }
293 if ( domid != xenstore_domid && xenstore_gmfn != -1)
294 {
295 gnttab[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access;
296 gnttab[GNTTAB_RESERVED_XENSTORE].domid = xenstore_domid;
297 gnttab[GNTTAB_RESERVED_XENSTORE].frame = xenstore_gmfn;
298 }
299
300 if ( munmap(gnttab, PAGE_SIZE) == -1 )
301 {
302 xc_dom_panic(xch, XC_INTERNAL_ERROR,
303 "%s: failed to unmap domU grant table "
304 "[errno=%d]\n",
305 __FUNCTION__, errno);
306 return -1;
307 }
308
309 /* Guest shouldn't really touch its grant table until it has
310 * enabled its caches. But lets be nice. */
311 xc_domain_cacheflush(xch, domid, gnttab_gmfn, 1);
312
313 return 0;
314 }
315
xc_dom_gnttab_hvm_seed(xc_interface * xch,uint32_t domid,xen_pfn_t console_gpfn,xen_pfn_t xenstore_gpfn,uint32_t console_domid,uint32_t xenstore_domid)316 int xc_dom_gnttab_hvm_seed(xc_interface *xch, uint32_t domid,
317 xen_pfn_t console_gpfn,
318 xen_pfn_t xenstore_gpfn,
319 uint32_t console_domid,
320 uint32_t xenstore_domid)
321 {
322 int rc;
323 xen_pfn_t scratch_gpfn;
324 struct xen_add_to_physmap xatp = {
325 .domid = domid,
326 .space = XENMAPSPACE_grant_table,
327 .idx = 0,
328 };
329 struct xen_remove_from_physmap xrfp = {
330 .domid = domid,
331 };
332
333 rc = xc_core_arch_get_scratch_gpfn(xch, domid, &scratch_gpfn);
334 if ( rc < 0 )
335 {
336 xc_dom_panic(xch, XC_INTERNAL_ERROR,
337 "%s: failed to get a scratch gfn "
338 "[errno=%d]\n",
339 __FUNCTION__, errno);
340 return -1;
341 }
342 xatp.gpfn = scratch_gpfn;
343 xrfp.gpfn = scratch_gpfn;
344
345 xc_dom_printf(xch, "%s: called, pfn=0x%"PRI_xen_pfn, __FUNCTION__,
346 scratch_gpfn);
347
348
349 rc = do_memory_op(xch, XENMEM_add_to_physmap, &xatp, sizeof(xatp));
350 if ( rc != 0 )
351 {
352 xc_dom_panic(xch, XC_INTERNAL_ERROR,
353 "%s: failed to add gnttab to physmap "
354 "[errno=%d]\n",
355 __FUNCTION__, errno);
356 return -1;
357 }
358
359 rc = xc_dom_gnttab_seed(xch, domid,
360 console_gpfn, xenstore_gpfn,
361 console_domid, xenstore_domid);
362 if (rc != 0)
363 {
364 xc_dom_panic(xch, XC_INTERNAL_ERROR,
365 "%s: failed to seed gnttab entries\n",
366 __FUNCTION__);
367 (void) do_memory_op(xch, XENMEM_remove_from_physmap, &xrfp, sizeof(xrfp));
368 return -1;
369 }
370
371 rc = do_memory_op(xch, XENMEM_remove_from_physmap, &xrfp, sizeof(xrfp));
372 if (rc != 0)
373 {
374 xc_dom_panic(xch, XC_INTERNAL_ERROR,
375 "%s: failed to remove gnttab from physmap "
376 "[errno=%d]\n",
377 __FUNCTION__, errno);
378 return -1;
379 }
380
381 return 0;
382 }
383
xc_dom_gnttab_init(struct xc_dom_image * dom)384 int xc_dom_gnttab_init(struct xc_dom_image *dom)
385 {
386 if ( xc_dom_translated(dom) ) {
387 return xc_dom_gnttab_hvm_seed(dom->xch, dom->guest_domid,
388 dom->console_pfn, dom->xenstore_pfn,
389 dom->console_domid, dom->xenstore_domid);
390 } else {
391 return xc_dom_gnttab_seed(dom->xch, dom->guest_domid,
392 xc_dom_p2m(dom, dom->console_pfn),
393 xc_dom_p2m(dom, dom->xenstore_pfn),
394 dom->console_domid, dom->xenstore_domid);
395 }
396 }
397
398 /*
399 * Local variables:
400 * mode: C
401 * c-file-style: "BSD"
402 * c-basic-offset: 4
403 * tab-width: 4
404 * indent-tabs-mode: nil
405 * End:
406 */
407