#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "init-dom-json.h" #define LAPIC_BASE_ADDRESS 0xfee00000UL #define MB(x) ((uint64_t)x << 20) #define GB(x) ((uint64_t)x << 30) static uint32_t domid = ~0; static char *kernel; static char *ramdisk; static char *flask; static char *param; static char *name = "Xenstore"; static int memory; static int maxmem; static xen_pfn_t console_gfn; static xc_evtchn_port_or_error_t console_evtchn; static xentoollog_level minmsglevel = XTL_PROGRESS; static void *logger; static struct option options[] = { { "kernel", 1, NULL, 'k' }, { "memory", 1, NULL, 'm' }, { "flask", 1, NULL, 'f' }, { "ramdisk", 1, NULL, 'r' }, { "param", 1, NULL, 'p' }, { "name", 1, NULL, 'n' }, { "maxmem", 1, NULL, 'M' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static void usage(void) { fprintf(stderr, "Usage:\n" "\n" "init-xenstore-domain \n" "\n" "where options may include:\n" "\n" " --kernel kernel file of the xenstore domain, mandatory\n" " --memory size of the domain in MB, mandatory\n" " --flask optional flask label of the domain\n" " --ramdisk optional ramdisk file for the domain\n" " --param optional additional parameters for the domain\n" " --name name of the domain (default: Xenstore)\n" " --maxmem maximum memory size in the format:\n" " |/|:/\n" " (an absolute value in MB, a fraction a/b of\n" " the host memory, or the maximum of both)\n" " -v[v[v]] verbosity of domain building\n"); } static int build(xc_interface *xch) { char cmdline[512]; int rv, xs_fd; struct xc_dom_image *dom = NULL; int limit_kb = (maxmem ? : memory) * 1024 + X86_HVM_NR_SPECIAL_PAGES * 4; uint64_t mem_size = MB(memory); uint64_t max_size = MB(maxmem ? : memory); struct e820entry e820[3]; struct xen_domctl_createdomain config = { .ssidref = SECINITSID_DOMU, .flags = XEN_DOMCTL_CDF_xs_domain, .max_vcpus = 1, .max_evtchn_port = -1, /* No limit. */ /* * 1 grant frame is enough: we don't need many grants. * Mini-OS doesn't like less than 4, though, so use 4. * 128 maptrack frames: 256 entries per frame, enough for 32768 domains. * Currently Mini-OS only supports grant v1. */ .max_grant_frames = 4, .max_maptrack_frames = 128, .grant_opts = XEN_DOMCTL_GRANT_version(1), }; xs_fd = open("/dev/xen/xenbus_backend", O_RDWR); if ( xs_fd == -1 ) { fprintf(stderr, "Could not open /dev/xen/xenbus_backend\n"); return -1; } if ( flask ) { rv = xc_flask_context_to_sid(xch, flask, strlen(flask), &config.ssidref); if ( rv ) { fprintf(stderr, "xc_flask_context_to_sid failed\n"); goto err; } } dom = xc_dom_allocate(xch, NULL, NULL); if ( !dom ) { fprintf(stderr, "xc_dom_allocate failed\n"); rv = -1; goto err; } rv = xc_dom_kernel_file(dom, kernel); if ( rv ) { fprintf(stderr, "xc_dom_kernel_file failed\n"); goto err; } if ( ramdisk ) { rv = xc_dom_module_file(dom, ramdisk, NULL); if ( rv ) { fprintf(stderr, "xc_dom_module_file failed\n"); goto err; } } rv = xc_dom_boot_xen_init(dom, xch, domid); if ( rv ) { fprintf(stderr, "xc_dom_boot_xen_init failed\n"); goto err; } /* * This is a bodge. We can't currently inspect the kernel's ELF notes * ahead of attempting to construct a domain, so try PVH first, suppressing * errors by setting min level to high, and fall back to PV. */ dom->container_type = XC_DOM_HVM_CONTAINER; xtl_stdiostream_set_minlevel(logger, XTL_CRITICAL); rv = xc_dom_parse_image(dom); xtl_stdiostream_set_minlevel(logger, minmsglevel); if ( rv ) { dom->container_type = XC_DOM_PV_CONTAINER; rv = xc_dom_parse_image(dom); if ( rv ) { /* Retry PVH, now with normal logging level. */ dom->container_type = XC_DOM_HVM_CONTAINER; rv = xc_dom_parse_image(dom); if ( rv ) { fprintf(stderr, "xc_dom_parse_image failed\n"); goto err; } } } if ( dom->container_type == XC_DOM_HVM_CONTAINER ) { config.flags |= XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap; config.arch.emulation_flags = XEN_X86_EMU_LAPIC; dom->target_pages = mem_size >> XC_PAGE_SHIFT; dom->mmio_size = GB(4) - LAPIC_BASE_ADDRESS; dom->lowmem_end = (mem_size > LAPIC_BASE_ADDRESS) ? LAPIC_BASE_ADDRESS : mem_size; dom->highmem_end = (mem_size > LAPIC_BASE_ADDRESS) ? GB(4) + mem_size - LAPIC_BASE_ADDRESS : 0; dom->mmio_start = LAPIC_BASE_ADDRESS; dom->max_vcpus = 1; e820[0].addr = 0; e820[0].size = (max_size > LAPIC_BASE_ADDRESS) ? LAPIC_BASE_ADDRESS : max_size; e820[0].type = E820_RAM; e820[1].addr = (X86_HVM_END_SPECIAL_REGION - X86_HVM_NR_SPECIAL_PAGES) << XC_PAGE_SHIFT; e820[1].size = X86_HVM_NR_SPECIAL_PAGES << XC_PAGE_SHIFT; e820[1].type = E820_RESERVED; e820[2].addr = GB(4); e820[2].size = (max_size > LAPIC_BASE_ADDRESS) ? max_size - LAPIC_BASE_ADDRESS : 0; e820[2].type = E820_RAM; } rv = xc_domain_create(xch, &domid, &config); if ( rv ) { fprintf(stderr, "xc_domain_create failed\n"); goto err; } rv = xc_domain_max_vcpus(xch, domid, config.max_vcpus); if ( rv ) { fprintf(stderr, "xc_domain_max_vcpus failed\n"); goto err; } rv = xc_domain_setmaxmem(xch, domid, limit_kb); if ( rv ) { fprintf(stderr, "xc_domain_setmaxmem failed\n"); goto err; } console_evtchn = xc_evtchn_alloc_unbound(xch, domid, 0); if ( console_evtchn < 0 ) { fprintf(stderr, "xc_evtchn_alloc_unbound failed\n"); goto err; } if ( dom->container_type == XC_DOM_PV_CONTAINER ) { rv = xc_domain_set_memmap_limit(xch, domid, limit_kb); if ( rv ) { fprintf(stderr, "xc_domain_set_memmap_limit failed\n"); goto err; } } rv = ioctl(xs_fd, IOCTL_XENBUS_BACKEND_SETUP, domid); if ( rv < 0 ) { fprintf(stderr, "Xenbus setup ioctl failed\n"); goto err; } if ( param ) snprintf(cmdline, 512, "--event %d %s", rv, param); else snprintf(cmdline, 512, "--event %d", rv); dom->guest_domid = domid; dom->cmdline = xc_dom_strdup(dom, cmdline); dom->xenstore_domid = domid; dom->console_evtchn = console_evtchn; rv = xc_dom_mem_init(dom, memory); if ( rv ) { fprintf(stderr, "xc_dom_mem_init failed\n"); goto err; } rv = xc_dom_boot_mem_init(dom); if ( rv ) { fprintf(stderr, "xc_dom_boot_mem_init failed\n"); goto err; } if ( dom->container_type == XC_DOM_HVM_CONTAINER ) { rv = xc_domain_set_memory_map(xch, domid, e820, dom->highmem_end ? 3 : 2); if ( rv ) { fprintf(stderr, "xc_domain_set_memory_map failed\n"); goto err; } } rv = xc_dom_build_image(dom); if ( rv ) { fprintf(stderr, "xc_dom_build_image failed\n"); goto err; } rv = xc_dom_boot_image(dom); if ( rv ) { fprintf(stderr, "xc_dom_boot_image failed\n"); goto err; } rv = xc_dom_gnttab_init(dom); if ( rv ) { fprintf(stderr, "xc_dom_gnttab_init failed\n"); goto err; } rv = xc_domain_set_virq_handler(xch, domid, VIRQ_DOM_EXC); if ( rv ) { fprintf(stderr, "xc_domain_set_virq_handler failed\n"); goto err; } rv = xc_domain_unpause(xch, domid); if ( rv ) { fprintf(stderr, "xc_domain_unpause failed\n"); goto err; } rv = 0; console_gfn = (dom->container_type == XC_DOM_PV_CONTAINER) ? xc_dom_p2m(dom, dom->console_pfn) : dom->console_pfn; err: if ( dom ) xc_dom_release(dom); if ( xs_fd >= 0 ) close(xs_fd); /* if we failed then destroy the domain */ if ( rv && domid != ~0 ) xc_domain_destroy(xch, domid); return rv; } static int check_domain(xc_interface *xch) { /* Commonly dom0 is the only domain, but buffer a little for efficiency. */ xc_domaininfo_t info[8]; uint32_t dom; int ret; dom = 1; while ( (ret = xc_domain_getinfolist(xch, dom, ARRAY_SIZE(info), info)) > 0 ) { for ( size_t i = 0; i < ret; i++ ) { if ( info[i].flags & XEN_DOMINF_xs_domain ) return 1; } dom = info[ret - 1].domain + 1; } if ( ret < 0 && errno != ESRCH ) { fprintf(stderr, "xc_domain_getinfo failed\n"); return ret; } return 0; } static int parse_maxmem(xc_interface *xch, char *str) { xc_physinfo_t info; int rv; unsigned long mb = 0, a = 0, b = 0; unsigned long val; unsigned long *res; char *p; char *s = str; rv = xc_physinfo(xch, &info); if ( rv ) { fprintf(stderr, "xc_physinfo failed\n"); return -1; } res = &mb; for (p = s; *p; s = p + 1) { val = strtoul(s, &p, 10); if ( val == 0 || val >= INT_MAX / 1024 ) goto err; if ( *p == '/' ) { if ( res != &mb || a != 0 ) goto err; a = val; res = &b; continue; } if ( *res != 0 ) goto err; *res = val; if ( *p != 0 && *p != ':' ) goto err; res = &mb; } if ( a && !b ) goto err; val = a ? info.total_pages * a / (b * 1024 * 1024 / XC_PAGE_SIZE) : 0; if ( val >= INT_MAX / 1024 ) goto err; maxmem = mb < val ? val : mb; if ( maxmem < memory ) maxmem = 0; return maxmem; err: fprintf(stderr, "illegal value for maxmem: %s\n", str); return -1; } static void do_xs_write(struct xs_handle *xsh, char *path, char *val) { if ( !xs_write(xsh, XBT_NULL, path, val, strlen(val)) ) fprintf(stderr, "writing %s to xenstore failed.\n", path); } static void do_xs_write_dom(struct xs_handle *xsh, char *path, char *val) { char full_path[64]; snprintf(full_path, 64, "/local/domain/%d/%s", domid, path); do_xs_write(xsh, full_path, val); } int main(int argc, char** argv) { int opt; xc_interface *xch; struct xs_handle *xsh; char buf[16]; int rv, fd; char *maxmem_str = NULL; libxl_ctx *ctx; while ( (opt = getopt_long(argc, argv, "v", options, NULL)) != -1 ) { switch ( opt ) { case 'k': kernel = optarg; break; case 'm': memory = strtol(optarg, NULL, 10); break; case 'f': flask = optarg; break; case 'r': ramdisk = optarg; break; case 'p': param = optarg; break; case 'n': name = optarg; break; case 'M': maxmem_str = optarg; break; case 'v': if ( minmsglevel ) minmsglevel--; break; default: usage(); return 2; } } if ( optind != argc || !kernel || !memory ) { usage(); return 2; } logger = xtl_createlogger_stdiostream(stderr, minmsglevel, 0); xch = xc_interface_open(logger, logger, 0); if ( !xch ) { fprintf(stderr, "xc_interface_open() failed\n"); rv = 1; goto out; } if ( maxmem_str ) { maxmem = parse_maxmem(xch, maxmem_str); if ( maxmem < 0 ) { xc_interface_close(xch); rv = 1; goto out; } } rv = check_domain(xch); if ( !rv ) rv = build(xch); else if ( rv > 0 ) fprintf(stderr, "xenstore domain already present.\n"); xc_interface_close(xch); if ( rv ) { rv = 1; goto out; } rv = gen_stub_json_config(domid, NULL); if ( rv ) { rv = 3; goto out; } xsh = xs_open(0); if ( !xsh ) { fprintf(stderr, "xs_open() failed.\n"); rv = 3; goto out; } snprintf(buf, 16, "%d", domid); do_xs_write(xsh, "/tool/xenstored/domid", buf); do_xs_write_dom(xsh, "domid", buf); do_xs_write_dom(xsh, "name", name); snprintf(buf, 16, "%d", memory * 1024); do_xs_write_dom(xsh, "memory/target", buf); if (maxmem) snprintf(buf, 16, "%d", maxmem * 1024); do_xs_write_dom(xsh, "memory/static-max", buf); xs_close(xsh); if ( libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, logger)) { fprintf(stderr, "libxl_ctx_alloc() failed.\n"); rv = 3; goto out; } libxl_console_add_xenstore(ctx, domid, 0, console_evtchn, console_gfn, NULL); libxl_ctx_free(ctx); fd = creat(XEN_RUN_DIR "/xenstored.pid", 0666); if ( fd < 0 ) { fprintf(stderr, "Creating " XEN_RUN_DIR "/xenstored.pid failed\n"); rv = 3; goto out; } rv = snprintf(buf, 16, "domid:%d\n", domid); rv = write(fd, buf, rv); close(fd); if ( rv < 0 ) { fprintf(stderr, "Writing domid to " XEN_RUN_DIR "/xenstored.pid failed\n"); rv = 3; goto out; } rv = 0; out: if ( logger ) xtl_logger_destroy(logger); return rv; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */