Files
xen/tools/helpers/init-xenstore-domain.c
Jan Beulich dc21138144 libxc: drop size parameter from xc_flask_context_to_sid()
Nul-terminated strings are passed in all cases, so the strlen() can very
well be invoked by the function itself. In preparation for a hypervisor
change also include the nul terminator in the size calculation.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Reviewed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
2026-07-06 09:41:45 +02:00

593 lines
15 KiB
C

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <xenctrl.h>
#include <xenguest.h>
#include <xenstore.h>
#include <xentoollog.h>
#include <libxl.h>
#include <xen/sys/xenbus_dev.h>
#include <xen-tools/common-macros.h>
#include <xen-xsm/flask/flask.h>
#include <xen/io/xenbus.h>
#include "init-dom-json.h"
#define LAPIC_BASE_ADDRESS 0xfee00000UL
static uint32_t domid = DOMID_ANY;
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 inline uint64_t mb_to_bytes(int mem)
{
return (uint64_t)mem << 20;
}
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 <options>\n"
"\n"
"where options may include:\n"
"\n"
" --kernel <xenstore-kernel> kernel file of the xenstore domain, mandatory\n"
" --memory <memory size> size of the domain in MB, mandatory\n"
" --flask <flask-label> optional flask label of the domain\n"
" --ramdisk <ramdisk-file> optional ramdisk file for the domain\n"
" --param <cmdline> optional additional parameters for the domain\n"
" --name <name> name of the domain (default: Xenstore)\n"
" --maxmem <max size> maximum memory size in the format:\n"
" <MB val>|<a>/<b>|<MB val>:<a>/<b>\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_to_bytes(memory);
uint64_t max_size = mb_to_bytes(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, &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_evtchn_alloc_unbound(xch, domid, domid);
if ( rv < 0 )
{
fprintf(stderr, "xc_evtchn_alloc_unbound failed\n");
goto err;
}
dom->xenstore_evtchn = rv;
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;
libxl_device_p9 p9 = { .backend_domid = 0,
.tag = "Xen",
.path = XEN_LIB_DIR"/xenstore",
.security_model = "none",
.type = LIBXL_P9_TYPE_XEN_9PFSD,
};
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_device_9pfs_add(ctx, domid, &p9, 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:
*/