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