1 #include <fcntl.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <getopt.h>
8 #include <sys/ioctl.h>
9 #include <sys/mman.h>
10 #include <xenctrl.h>
11 #include <xenguest.h>
12 #include <xenstore.h>
13 #include <xentoollog.h>
14 #include <libxl.h>
15 #include <xen/sys/xenbus_dev.h>
16 #include <xen-tools/common-macros.h>
17 #include <xen-xsm/flask/flask.h>
18 #include <xen/io/xenbus.h>
19 
20 #include "init-dom-json.h"
21 
22 #define LAPIC_BASE_ADDRESS  0xfee00000UL
23 
24 static uint32_t domid = ~0;
25 static char *kernel;
26 static char *ramdisk;
27 static char *flask;
28 static char *param;
29 static char *name = "Xenstore";
30 static int memory;
31 static int maxmem;
32 static xen_pfn_t console_gfn;
33 static xc_evtchn_port_or_error_t console_evtchn;
34 static xentoollog_level minmsglevel = XTL_PROGRESS;
35 static void *logger;
36 
mb_to_bytes(int mem)37 static inline uint64_t mb_to_bytes(int mem)
38 {
39     return (uint64_t)mem << 20;
40 }
41 
42 static struct option options[] = {
43     { "kernel", 1, NULL, 'k' },
44     { "memory", 1, NULL, 'm' },
45     { "flask", 1, NULL, 'f' },
46     { "ramdisk", 1, NULL, 'r' },
47     { "param", 1, NULL, 'p' },
48     { "name", 1, NULL, 'n' },
49     { "maxmem", 1, NULL, 'M' },
50     { "verbose", 0, NULL, 'v' },
51     { NULL, 0, NULL, 0 }
52 };
53 
usage(void)54 static void usage(void)
55 {
56     fprintf(stderr,
57 "Usage:\n"
58 "\n"
59 "init-xenstore-domain <options>\n"
60 "\n"
61 "where options may include:\n"
62 "\n"
63 "  --kernel <xenstore-kernel> kernel file of the xenstore domain, mandatory\n"
64 "  --memory <memory size>     size of the domain in MB, mandatory\n"
65 "  --flask <flask-label>      optional flask label of the domain\n"
66 "  --ramdisk <ramdisk-file>   optional ramdisk file for the domain\n"
67 "  --param <cmdline>          optional additional parameters for the domain\n"
68 "  --name <name>              name of the domain (default: Xenstore)\n"
69 "  --maxmem <max size>        maximum memory size in the format:\n"
70 "                             <MB val>|<a>/<b>|<MB val>:<a>/<b>\n"
71 "                             (an absolute value in MB, a fraction a/b of\n"
72 "                             the host memory, or the maximum of both)\n"
73 "  -v[v[v]]                   verbosity of domain building\n");
74 }
75 
build(xc_interface * xch)76 static int build(xc_interface *xch)
77 {
78     char cmdline[512];
79     int rv, xs_fd;
80     struct xc_dom_image *dom = NULL;
81     int limit_kb = (maxmem ? : memory) * 1024 + X86_HVM_NR_SPECIAL_PAGES * 4;
82     uint64_t mem_size = mb_to_bytes(memory);
83     uint64_t max_size = mb_to_bytes(maxmem ? : memory);
84     struct e820entry e820[3];
85     struct xen_domctl_createdomain config = {
86         .ssidref = SECINITSID_DOMU,
87         .flags = XEN_DOMCTL_CDF_xs_domain,
88         .max_vcpus = 1,
89         .max_evtchn_port = -1, /* No limit. */
90 
91         /*
92          * 1 grant frame is enough: we don't need many grants.
93          * Mini-OS doesn't like less than 4, though, so use 4.
94          * 128 maptrack frames: 256 entries per frame, enough for 32768 domains.
95          * Currently Mini-OS only supports grant v1.
96          */
97         .max_grant_frames = 4,
98         .max_maptrack_frames = 128,
99         .grant_opts = XEN_DOMCTL_GRANT_version(1),
100     };
101 
102     xs_fd = open("/dev/xen/xenbus_backend", O_RDWR);
103     if ( xs_fd == -1 )
104     {
105         fprintf(stderr, "Could not open /dev/xen/xenbus_backend\n");
106         return -1;
107     }
108 
109     if ( flask )
110     {
111         rv = xc_flask_context_to_sid(xch, flask, strlen(flask), &config.ssidref);
112         if ( rv )
113         {
114             fprintf(stderr, "xc_flask_context_to_sid failed\n");
115             goto err;
116         }
117     }
118 
119     dom = xc_dom_allocate(xch, NULL, NULL);
120     if ( !dom )
121     {
122         fprintf(stderr, "xc_dom_allocate failed\n");
123         rv = -1;
124         goto err;
125     }
126 
127     rv = xc_dom_kernel_file(dom, kernel);
128     if ( rv )
129     {
130         fprintf(stderr, "xc_dom_kernel_file failed\n");
131         goto err;
132     }
133 
134     if ( ramdisk )
135     {
136         rv = xc_dom_module_file(dom, ramdisk, NULL);
137         if ( rv )
138         {
139             fprintf(stderr, "xc_dom_module_file failed\n");
140             goto err;
141         }
142     }
143 
144     rv = xc_dom_boot_xen_init(dom, xch, domid);
145     if ( rv )
146     {
147         fprintf(stderr, "xc_dom_boot_xen_init failed\n");
148         goto err;
149     }
150 
151     /*
152      * This is a bodge.  We can't currently inspect the kernel's ELF notes
153      * ahead of attempting to construct a domain, so try PVH first, suppressing
154      * errors by setting min level to high, and fall back to PV.
155      */
156     dom->container_type = XC_DOM_HVM_CONTAINER;
157     xtl_stdiostream_set_minlevel(logger, XTL_CRITICAL);
158     rv = xc_dom_parse_image(dom);
159     xtl_stdiostream_set_minlevel(logger, minmsglevel);
160     if ( rv )
161     {
162         dom->container_type = XC_DOM_PV_CONTAINER;
163         rv = xc_dom_parse_image(dom);
164         if ( rv )
165         {
166             /* Retry PVH, now with normal logging level. */
167             dom->container_type = XC_DOM_HVM_CONTAINER;
168             rv = xc_dom_parse_image(dom);
169             if ( rv )
170             {
171                 fprintf(stderr, "xc_dom_parse_image failed\n");
172                 goto err;
173             }
174         }
175     }
176 
177     if ( dom->container_type == XC_DOM_HVM_CONTAINER )
178     {
179         config.flags |= XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap;
180         config.arch.emulation_flags = XEN_X86_EMU_LAPIC;
181         dom->target_pages = mem_size >> XC_PAGE_SHIFT;
182         dom->mmio_size = GB(4) - LAPIC_BASE_ADDRESS;
183         dom->lowmem_end = (mem_size > LAPIC_BASE_ADDRESS) ?
184                           LAPIC_BASE_ADDRESS : mem_size;
185         dom->highmem_end = (mem_size > LAPIC_BASE_ADDRESS) ?
186                            GB(4) + mem_size - LAPIC_BASE_ADDRESS : 0;
187         dom->mmio_start = LAPIC_BASE_ADDRESS;
188         dom->max_vcpus = 1;
189         e820[0].addr = 0;
190         e820[0].size = (max_size > LAPIC_BASE_ADDRESS) ?
191                        LAPIC_BASE_ADDRESS : max_size;
192         e820[0].type = E820_RAM;
193         e820[1].addr = (X86_HVM_END_SPECIAL_REGION -
194                         X86_HVM_NR_SPECIAL_PAGES) << XC_PAGE_SHIFT;
195         e820[1].size = X86_HVM_NR_SPECIAL_PAGES << XC_PAGE_SHIFT;
196         e820[1].type = E820_RESERVED;
197         e820[2].addr = GB(4);
198         e820[2].size = (max_size > LAPIC_BASE_ADDRESS) ?
199                        max_size - LAPIC_BASE_ADDRESS : 0;
200         e820[2].type = E820_RAM;
201     }
202 
203     rv = xc_domain_create(xch, &domid, &config);
204     if ( rv )
205     {
206         fprintf(stderr, "xc_domain_create failed\n");
207         goto err;
208     }
209     rv = xc_domain_max_vcpus(xch, domid, config.max_vcpus);
210     if ( rv )
211     {
212         fprintf(stderr, "xc_domain_max_vcpus failed\n");
213         goto err;
214     }
215     rv = xc_domain_setmaxmem(xch, domid, limit_kb);
216     if ( rv )
217     {
218         fprintf(stderr, "xc_domain_setmaxmem failed\n");
219         goto err;
220     }
221     console_evtchn = xc_evtchn_alloc_unbound(xch, domid, 0);
222     if ( console_evtchn < 0 )
223     {
224         fprintf(stderr, "xc_evtchn_alloc_unbound failed\n");
225         goto err;
226     }
227 
228     if ( dom->container_type == XC_DOM_PV_CONTAINER )
229     {
230         rv = xc_domain_set_memmap_limit(xch, domid, limit_kb);
231         if ( rv )
232         {
233             fprintf(stderr, "xc_domain_set_memmap_limit failed\n");
234             goto err;
235         }
236     }
237 
238     rv = ioctl(xs_fd, IOCTL_XENBUS_BACKEND_SETUP, domid);
239     if ( rv < 0 )
240     {
241         fprintf(stderr, "Xenbus setup ioctl failed\n");
242         goto err;
243     }
244 
245     if ( param )
246         snprintf(cmdline, 512, "--event %d %s", rv, param);
247     else
248         snprintf(cmdline, 512, "--event %d", rv);
249 
250     dom->guest_domid = domid;
251     dom->cmdline = xc_dom_strdup(dom, cmdline);
252     dom->xenstore_domid = domid;
253     dom->console_evtchn = console_evtchn;
254     rv = xc_evtchn_alloc_unbound(xch, domid, domid);
255     if ( rv < 0 )
256     {
257         fprintf(stderr, "xc_evtchn_alloc_unbound failed\n");
258         goto err;
259     }
260     dom->xenstore_evtchn = rv;
261 
262     rv = xc_dom_mem_init(dom, memory);
263     if ( rv )
264     {
265         fprintf(stderr, "xc_dom_mem_init failed\n");
266         goto err;
267     }
268     rv = xc_dom_boot_mem_init(dom);
269     if ( rv )
270     {
271         fprintf(stderr, "xc_dom_boot_mem_init failed\n");
272         goto err;
273     }
274     if ( dom->container_type == XC_DOM_HVM_CONTAINER )
275     {
276         rv = xc_domain_set_memory_map(xch, domid, e820,
277                                       dom->highmem_end ? 3 : 2);
278         if ( rv )
279         {
280             fprintf(stderr, "xc_domain_set_memory_map failed\n");
281             goto err;
282         }
283     }
284     rv = xc_dom_build_image(dom);
285     if ( rv )
286     {
287         fprintf(stderr, "xc_dom_build_image failed\n");
288         goto err;
289     }
290     rv = xc_dom_boot_image(dom);
291     if ( rv )
292     {
293         fprintf(stderr, "xc_dom_boot_image failed\n");
294         goto err;
295     }
296     rv = xc_dom_gnttab_init(dom);
297     if ( rv )
298     {
299         fprintf(stderr, "xc_dom_gnttab_init failed\n");
300         goto err;
301     }
302 
303     rv = xc_domain_set_virq_handler(xch, domid, VIRQ_DOM_EXC);
304     if ( rv )
305     {
306         fprintf(stderr, "xc_domain_set_virq_handler failed\n");
307         goto err;
308     }
309     rv = xc_domain_unpause(xch, domid);
310     if ( rv )
311     {
312         fprintf(stderr, "xc_domain_unpause failed\n");
313         goto err;
314     }
315 
316     rv = 0;
317     console_gfn = (dom->container_type == XC_DOM_PV_CONTAINER)
318                   ? xc_dom_p2m(dom, dom->console_pfn)
319                   : dom->console_pfn;
320 
321 err:
322     if ( dom )
323         xc_dom_release(dom);
324     if ( xs_fd >= 0 )
325         close(xs_fd);
326 
327     /* if we failed then destroy the domain */
328     if ( rv && domid != ~0 )
329         xc_domain_destroy(xch, domid);
330 
331     return rv;
332 }
333 
check_domain(xc_interface * xch)334 static int check_domain(xc_interface *xch)
335 {
336     /* Commonly dom0 is the only domain, but buffer a little for efficiency. */
337     xc_domaininfo_t info[8];
338     uint32_t dom;
339     int ret;
340 
341     dom = 1;
342     while ( (ret = xc_domain_getinfolist(xch, dom, ARRAY_SIZE(info), info)) > 0 )
343     {
344         for ( size_t i = 0; i < ret; i++ )
345         {
346             if ( info[i].flags & XEN_DOMINF_xs_domain )
347                 return 1;
348         }
349         dom = info[ret - 1].domain + 1;
350     }
351     if ( ret < 0 && errno != ESRCH )
352     {
353         fprintf(stderr, "xc_domain_getinfo failed\n");
354         return ret;
355     }
356 
357     return 0;
358 }
359 
parse_maxmem(xc_interface * xch,char * str)360 static int parse_maxmem(xc_interface *xch, char *str)
361 {
362     xc_physinfo_t info;
363     int rv;
364     unsigned long mb = 0, a = 0, b = 0;
365     unsigned long val;
366     unsigned long *res;
367     char *p;
368     char *s = str;
369 
370     rv = xc_physinfo(xch, &info);
371     if ( rv )
372     {
373         fprintf(stderr, "xc_physinfo failed\n");
374         return -1;
375     }
376 
377     res = &mb;
378     for (p = s; *p; s = p + 1)
379     {
380         val = strtoul(s, &p, 10);
381         if ( val == 0 || val >= INT_MAX / 1024 )
382             goto err;
383         if ( *p == '/' )
384         {
385             if ( res != &mb || a != 0 )
386                 goto err;
387             a = val;
388             res = &b;
389             continue;
390         }
391         if ( *res != 0 )
392             goto err;
393         *res = val;
394         if ( *p != 0 && *p != ':' )
395             goto err;
396         res = &mb;
397     }
398     if ( a && !b )
399         goto err;
400 
401     val = a ? info.total_pages * a / (b * 1024 * 1024 / XC_PAGE_SIZE) : 0;
402     if ( val >= INT_MAX / 1024 )
403         goto err;
404 
405     maxmem = mb < val ? val : mb;
406     if ( maxmem < memory )
407         maxmem = 0;
408 
409     return maxmem;
410 
411 err:
412     fprintf(stderr, "illegal value for maxmem: %s\n", str);
413     return -1;
414 }
415 
do_xs_write(struct xs_handle * xsh,char * path,char * val)416 static void do_xs_write(struct xs_handle *xsh, char *path, char *val)
417 {
418     if ( !xs_write(xsh, XBT_NULL, path, val, strlen(val)) )
419         fprintf(stderr, "writing %s to xenstore failed.\n", path);
420 }
421 
do_xs_write_dom(struct xs_handle * xsh,char * path,char * val)422 static void do_xs_write_dom(struct xs_handle *xsh, char *path, char *val)
423 {
424     char full_path[64];
425 
426     snprintf(full_path, 64, "/local/domain/%d/%s", domid, path);
427     do_xs_write(xsh, full_path, val);
428 }
429 
main(int argc,char ** argv)430 int main(int argc, char** argv)
431 {
432     int opt;
433     xc_interface *xch;
434     struct xs_handle *xsh;
435     char buf[16];
436     int rv, fd;
437     char *maxmem_str = NULL;
438     libxl_ctx *ctx;
439     libxl_device_p9 p9 = { .backend_domid = 0,
440                            .tag = "Xen",
441                            .path = XEN_LIB_DIR"/xenstore",
442                            .security_model = "none",
443                            .type = LIBXL_P9_TYPE_XEN_9PFSD,
444     };
445 
446     while ( (opt = getopt_long(argc, argv, "v", options, NULL)) != -1 )
447     {
448         switch ( opt )
449         {
450         case 'k':
451             kernel = optarg;
452             break;
453         case 'm':
454             memory = strtol(optarg, NULL, 10);
455             break;
456         case 'f':
457             flask = optarg;
458             break;
459         case 'r':
460             ramdisk = optarg;
461             break;
462         case 'p':
463             param = optarg;
464             break;
465         case 'n':
466             name = optarg;
467             break;
468         case 'M':
469             maxmem_str = optarg;
470             break;
471         case 'v':
472             if ( minmsglevel )
473                 minmsglevel--;
474             break;
475         default:
476             usage();
477             return 2;
478         }
479     }
480 
481     if ( optind != argc || !kernel || !memory )
482     {
483         usage();
484         return 2;
485     }
486 
487     logger = xtl_createlogger_stdiostream(stderr, minmsglevel, 0);
488     xch = xc_interface_open(logger, logger, 0);
489     if ( !xch )
490     {
491         fprintf(stderr, "xc_interface_open() failed\n");
492         rv = 1;
493         goto out;
494     }
495 
496     if ( maxmem_str )
497     {
498         maxmem = parse_maxmem(xch, maxmem_str);
499         if ( maxmem < 0 )
500         {
501             xc_interface_close(xch);
502             rv = 1;
503             goto out;
504         }
505     }
506 
507     rv = check_domain(xch);
508 
509     if ( !rv )
510         rv = build(xch);
511     else if ( rv > 0 )
512         fprintf(stderr, "xenstore domain already present.\n");
513 
514     xc_interface_close(xch);
515 
516     if ( rv )
517     {
518         rv = 1;
519         goto out;
520     }
521 
522     rv = gen_stub_json_config(domid, NULL);
523     if ( rv )
524     {
525         rv = 3;
526         goto out;
527     }
528 
529     xsh = xs_open(0);
530     if ( !xsh )
531     {
532         fprintf(stderr, "xs_open() failed.\n");
533         rv = 3;
534         goto out;
535     }
536     snprintf(buf, 16, "%d", domid);
537     do_xs_write(xsh, "/tool/xenstored/domid", buf);
538     do_xs_write_dom(xsh, "domid", buf);
539     do_xs_write_dom(xsh, "name", name);
540     snprintf(buf, 16, "%d", memory * 1024);
541     do_xs_write_dom(xsh, "memory/target", buf);
542     if (maxmem)
543         snprintf(buf, 16, "%d", maxmem * 1024);
544     do_xs_write_dom(xsh, "memory/static-max", buf);
545     xs_close(xsh);
546 
547     if ( libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, logger))
548     {
549         fprintf(stderr, "libxl_ctx_alloc() failed.\n");
550         rv = 3;
551         goto out;
552     }
553     libxl_console_add_xenstore(ctx, domid, 0, console_evtchn, console_gfn,
554                                NULL);
555     libxl_device_9pfs_add(ctx, domid, &p9, NULL);
556     libxl_ctx_free(ctx);
557 
558     fd = creat(XEN_RUN_DIR "/xenstored.pid", 0666);
559     if ( fd < 0 )
560     {
561         fprintf(stderr, "Creating " XEN_RUN_DIR "/xenstored.pid failed\n");
562         rv = 3;
563         goto out;
564     }
565     rv = snprintf(buf, 16, "domid:%d\n", domid);
566     rv = write(fd, buf, rv);
567     close(fd);
568     if ( rv < 0 )
569     {
570         fprintf(stderr,
571                 "Writing domid to " XEN_RUN_DIR "/xenstored.pid failed\n");
572         rv = 3;
573         goto out;
574     }
575 
576     rv = 0;
577 
578  out:
579     if ( logger )
580         xtl_logger_destroy(logger);
581 
582     return rv;
583 }
584 
585 /*
586  * Local variables:
587  * mode: C
588  * c-file-style: "BSD"
589  * c-basic-offset: 4
590  * indent-tabs-mode: nil
591  * End:
592  */
593