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