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