mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Very old glibc (2.4 in the observed case) declares strndup() only when
_GNU_SOURCE is defined.
Fixes: ad860c6454 ("tools/xl: add xl commands for xenstore quota operations")
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Anthony PERARD <anthony.perard@vates.tech>
3256 lines
114 KiB
C
3256 lines
114 KiB
C
/*
|
|
* Copyright 2009-2017 Citrix Ltd and other contributors
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation; version 2.1 only. with the special
|
|
* exception on linking described in file LICENSE.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <xen/hvm/e820.h>
|
|
#include <xen/hvm/params.h>
|
|
#include <xen/io/sndif.h>
|
|
#include <xen/io/kbdif.h>
|
|
|
|
#include <libxl.h>
|
|
#include <libxl_utils.h>
|
|
#include <libxlutil.h>
|
|
|
|
#include "xl.h"
|
|
#include "xl_utils.h"
|
|
#include "xl_parse.h"
|
|
|
|
extern void set_default_nic_values(libxl_device_nic *nic);
|
|
|
|
#define ARRAY_EXTEND_INIT__CORE(array,count,initfn,more) \
|
|
({ \
|
|
typeof((count)) array_extend_old_count = (count); \
|
|
(count)++; \
|
|
(array) = xrealloc((array), sizeof(*array) * (count)); \
|
|
(initfn)(&(array)[array_extend_old_count]); \
|
|
more; \
|
|
&(array)[array_extend_old_count]; \
|
|
})
|
|
|
|
#define ARRAY_EXTEND_INIT(array,count,initfn) \
|
|
ARRAY_EXTEND_INIT__CORE((array),(count),(initfn), ({ \
|
|
(array)[array_extend_old_count].devid = array_extend_old_count; \
|
|
}))
|
|
|
|
#define ARRAY_EXTEND_INIT_NODEVID(array,count,initfn) \
|
|
ARRAY_EXTEND_INIT__CORE((array),(count),(initfn), /* nothing */ )
|
|
|
|
static const char *action_on_shutdown_names[] = {
|
|
[LIBXL_ACTION_ON_SHUTDOWN_DESTROY] = "destroy",
|
|
|
|
[LIBXL_ACTION_ON_SHUTDOWN_RESTART] = "restart",
|
|
[LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME] = "rename-restart",
|
|
|
|
[LIBXL_ACTION_ON_SHUTDOWN_PRESERVE] = "preserve",
|
|
|
|
[LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY] = "coredump-destroy",
|
|
[LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART] = "coredump-restart",
|
|
|
|
[LIBXL_ACTION_ON_SHUTDOWN_SOFT_RESET] = "soft-reset",
|
|
};
|
|
|
|
const char *get_action_on_shutdown_name(libxl_action_on_shutdown a)
|
|
{
|
|
return action_on_shutdown_names[a];
|
|
}
|
|
|
|
static int parse_action_on_shutdown(const char *buf, libxl_action_on_shutdown *a)
|
|
{
|
|
int i;
|
|
const char *n;
|
|
|
|
for (i = 0; i < sizeof(action_on_shutdown_names) / sizeof(action_on_shutdown_names[0]); i++) {
|
|
n = action_on_shutdown_names[i];
|
|
|
|
if (!n) continue;
|
|
|
|
if (strcmp(buf, n) == 0) {
|
|
*a = i;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define DSTATE_INITIAL 0
|
|
#define DSTATE_TAP 1
|
|
#define DSTATE_PHYSPATH 2
|
|
#define DSTATE_VIRTPATH 3
|
|
#define DSTATE_VIRTTYPE 4
|
|
#define DSTATE_RW 5
|
|
#define DSTATE_TERMINAL 6
|
|
|
|
void parse_disk_config_multistring(XLU_Config **config,
|
|
int nspecs, const char *const *specs,
|
|
libxl_device_disk *disk)
|
|
{
|
|
int e;
|
|
|
|
libxl_device_disk_init(disk);
|
|
|
|
if (!*config) {
|
|
*config = xlu_cfg_init(stderr, "command line");
|
|
if (!*config) { perror("xlu_cfg_init"); exit(-1); }
|
|
}
|
|
|
|
e = xlu_disk_parse(*config, nspecs, specs, disk);
|
|
if (e == EINVAL) exit(EXIT_FAILURE);
|
|
if (e) {
|
|
fprintf(stderr,"xlu_disk_parse failed: %s\n",strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void parse_disk_config(XLU_Config **config, const char *spec,
|
|
libxl_device_disk *disk)
|
|
{
|
|
parse_disk_config_multistring(config, 1, &spec, disk);
|
|
}
|
|
|
|
static void parse_vif_rate(XLU_Config **config, const char *rate,
|
|
libxl_device_nic *nic)
|
|
{
|
|
int e;
|
|
|
|
e = xlu_vif_parse_rate(*config, rate, nic);
|
|
if (e == EINVAL || e == EOVERFLOW) exit(EXIT_FAILURE);
|
|
if (e) {
|
|
fprintf(stderr,"xlu_vif_parse_rate failed: %s\n",strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
int parse_range(const char *str, unsigned long *a, unsigned long *b)
|
|
{
|
|
const char *nstr;
|
|
char *endptr;
|
|
|
|
*a = *b = strtoul(str, &endptr, 10);
|
|
if (endptr == str || *a == ULONG_MAX)
|
|
return 1;
|
|
|
|
if (*endptr == '-') {
|
|
nstr = endptr + 1;
|
|
|
|
*b = strtoul(nstr, &endptr, 10);
|
|
if (endptr == nstr || *b == ULONG_MAX || *b < *a)
|
|
return 1;
|
|
}
|
|
|
|
/* Valid value or range so far, but we also don't want junk after that */
|
|
if (*endptr != '\0')
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Add or removes a specific set of cpus (specified in str, either as
|
|
* single cpus or as entire NUMA nodes) to/from cpumap.
|
|
*/
|
|
static int update_cpumap_range(const char *str, libxl_bitmap *cpumap)
|
|
{
|
|
unsigned long ida, idb;
|
|
libxl_bitmap node_cpumap;
|
|
bool is_not = false, is_nodes = false;
|
|
int rc = 0;
|
|
|
|
libxl_bitmap_init(&node_cpumap);
|
|
|
|
rc = libxl_node_bitmap_alloc(ctx, &node_cpumap, 0);
|
|
if (rc) {
|
|
fprintf(stderr, "libxl_node_bitmap_alloc failed.\n");
|
|
goto out;
|
|
}
|
|
|
|
/* Are we adding or removing cpus/nodes? */
|
|
if (STR_SKIP_PREFIX(str, "^")) {
|
|
is_not = true;
|
|
}
|
|
|
|
/* Are we dealing with cpus or full nodes? */
|
|
if (STR_SKIP_PREFIX(str, "node:") || STR_SKIP_PREFIX(str, "nodes:")) {
|
|
is_nodes = true;
|
|
}
|
|
|
|
if (strcmp(str, "all") == 0) {
|
|
/* We do not accept "^all" or "^nodes:all" */
|
|
if (is_not) {
|
|
fprintf(stderr, "Can't combine \"^\" and \"all\".\n");
|
|
rc = ERROR_INVAL;
|
|
} else
|
|
libxl_bitmap_set_any(cpumap);
|
|
goto out;
|
|
}
|
|
|
|
rc = parse_range(str, &ida, &idb);
|
|
if (rc) {
|
|
fprintf(stderr, "Invalid pcpu range: %s.\n", str);
|
|
goto out;
|
|
}
|
|
|
|
/* Add or remove the specified cpus in the range */
|
|
while (ida <= idb) {
|
|
if (is_nodes) {
|
|
/* Add/Remove all the cpus of a NUMA node */
|
|
int i;
|
|
|
|
rc = libxl_node_to_cpumap(ctx, ida, &node_cpumap);
|
|
if (rc) {
|
|
fprintf(stderr, "libxl_node_to_cpumap failed.\n");
|
|
goto out;
|
|
}
|
|
|
|
/* Add/Remove all the cpus in the node cpumap */
|
|
libxl_for_each_set_bit(i, node_cpumap) {
|
|
is_not ? libxl_bitmap_reset(cpumap, i) :
|
|
libxl_bitmap_set(cpumap, i);
|
|
}
|
|
} else {
|
|
/* Add/Remove this cpu */
|
|
is_not ? libxl_bitmap_reset(cpumap, ida) :
|
|
libxl_bitmap_set(cpumap, ida);
|
|
}
|
|
ida++;
|
|
}
|
|
|
|
out:
|
|
libxl_bitmap_dispose(&node_cpumap);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Takes a string representing a set of cpus (specified either as
|
|
* single cpus or as eintire NUMA nodes) and turns it into the
|
|
* corresponding libxl_bitmap (in cpumap).
|
|
*/
|
|
int parse_cpurange(const char *cpu, libxl_bitmap *cpumap)
|
|
{
|
|
char *ptr, *saveptr = NULL, *buf = xstrdup(cpu);
|
|
int rc = 0;
|
|
|
|
for (ptr = strtok_r(buf, ",", &saveptr); ptr;
|
|
ptr = strtok_r(NULL, ",", &saveptr)) {
|
|
rc = update_cpumap_range(ptr, cpumap);
|
|
if (rc)
|
|
break;
|
|
}
|
|
free(buf);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void parse_top_level_vnc_options(XLU_Config *config,
|
|
libxl_vnc_info *vnc)
|
|
{
|
|
long l;
|
|
|
|
xlu_cfg_get_defbool(config, "vnc", &vnc->enable, 0);
|
|
xlu_cfg_replace_string (config, "vnclisten", &vnc->listen, 0);
|
|
xlu_cfg_replace_string (config, "vncpasswd", &vnc->passwd, 0);
|
|
if (!xlu_cfg_get_long (config, "vncdisplay", &l, 0))
|
|
vnc->display = l;
|
|
xlu_cfg_get_defbool(config, "vncunused", &vnc->findunused, 0);
|
|
}
|
|
|
|
static void parse_top_level_sdl_options(XLU_Config *config,
|
|
libxl_sdl_info *sdl)
|
|
{
|
|
xlu_cfg_get_defbool(config, "sdl", &sdl->enable, 0);
|
|
xlu_cfg_get_defbool(config, "opengl", &sdl->opengl, 0);
|
|
xlu_cfg_replace_string (config, "display", &sdl->display, 0);
|
|
xlu_cfg_replace_string (config, "xauthority", &sdl->xauthority, 0);
|
|
}
|
|
|
|
static char *parse_cmdline(XLU_Config *config)
|
|
{
|
|
char *cmdline = NULL;
|
|
const char *root = NULL, *extra = NULL, *buf = NULL;
|
|
|
|
xlu_cfg_get_string (config, "cmdline", &buf, 0);
|
|
xlu_cfg_get_string (config, "root", &root, 0);
|
|
xlu_cfg_get_string (config, "extra", &extra, 0);
|
|
|
|
if (buf) {
|
|
cmdline = strdup(buf);
|
|
if (root || extra)
|
|
fprintf(stderr, "Warning: ignoring root= and extra= "
|
|
"in favour of cmdline=\n");
|
|
} else {
|
|
if (root && extra) {
|
|
xasprintf(&cmdline, "root=%s %s", root, extra);
|
|
} else if (root) {
|
|
xasprintf(&cmdline, "root=%s", root);
|
|
} else if (extra) {
|
|
cmdline = strdup(extra);
|
|
}
|
|
}
|
|
|
|
if ((buf || root || extra) && !cmdline) {
|
|
fprintf(stderr, "Failed to allocate memory for cmdline\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return cmdline;
|
|
}
|
|
|
|
static void parse_vcpu_affinity(libxl_domain_build_info *b_info,
|
|
XLU_ConfigList *cpus, const char *buf,
|
|
int num_cpus, bool is_hard)
|
|
{
|
|
libxl_bitmap *vcpu_affinity_array;
|
|
|
|
/*
|
|
* If we are here, and buf is !NULL, we're dealing with a string. What
|
|
* we do in this case is parse it, and copy the result in _all_ (up to
|
|
* b_info->max_vcpus) the elements of the vcpu affinity array.
|
|
*
|
|
* If buf is NULL, we have a list, and what we do is putting in the
|
|
* i-eth element of the vcpu affinity array the result of the parsing
|
|
* of the i-eth entry of the list. If there are more vcpus than
|
|
* entries, it is fine to just not touch the last array elements.
|
|
*/
|
|
|
|
/* Silently ignore values corresponding to non existing vcpus */
|
|
if (buf || num_cpus > b_info->max_vcpus)
|
|
num_cpus = b_info->max_vcpus;
|
|
|
|
if (is_hard) {
|
|
b_info->num_vcpu_hard_affinity = num_cpus;
|
|
b_info->vcpu_hard_affinity = xmalloc(num_cpus * sizeof(libxl_bitmap));
|
|
vcpu_affinity_array = b_info->vcpu_hard_affinity;
|
|
} else {
|
|
b_info->num_vcpu_soft_affinity = num_cpus;
|
|
b_info->vcpu_soft_affinity = xmalloc(num_cpus * sizeof(libxl_bitmap));
|
|
vcpu_affinity_array = b_info->vcpu_soft_affinity;
|
|
}
|
|
|
|
if (!buf) {
|
|
int j = 0;
|
|
|
|
while ((buf = xlu_cfg_get_listitem(cpus, j)) != NULL && j < num_cpus) {
|
|
libxl_bitmap_init(&vcpu_affinity_array[j]);
|
|
if (libxl_cpu_bitmap_alloc(ctx, &vcpu_affinity_array[j], 0)) {
|
|
fprintf(stderr, "Unable to allocate cpumap for vcpu %d\n", j);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (parse_cpurange(buf, &vcpu_affinity_array[j]))
|
|
exit(EXIT_FAILURE);
|
|
|
|
j++;
|
|
}
|
|
|
|
/* When we have a list of cpumaps, always disable automatic placement */
|
|
libxl_defbool_set(&b_info->numa_placement, false);
|
|
} else {
|
|
int i;
|
|
|
|
libxl_bitmap_init(&vcpu_affinity_array[0]);
|
|
if (libxl_cpu_bitmap_alloc(ctx, &vcpu_affinity_array[0], 0)) {
|
|
fprintf(stderr, "Unable to allocate cpumap for vcpu 0\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (parse_cpurange(buf, &vcpu_affinity_array[0]))
|
|
exit(EXIT_FAILURE);
|
|
|
|
for (i = 1; i < b_info->max_vcpus; i++) {
|
|
libxl_bitmap_init(&vcpu_affinity_array[i]);
|
|
if (libxl_cpu_bitmap_alloc(ctx, &vcpu_affinity_array[i], 0)) {
|
|
fprintf(stderr, "Unable to allocate cpumap for vcpu %d\n", i);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
libxl_bitmap_copy(ctx, &vcpu_affinity_array[i],
|
|
&vcpu_affinity_array[0]);
|
|
}
|
|
|
|
/* We have soft affinity already, disable automatic placement */
|
|
if (!is_hard)
|
|
libxl_defbool_set(&b_info->numa_placement, false);
|
|
}
|
|
}
|
|
|
|
static unsigned long parse_ulong(const char *str)
|
|
{
|
|
char *endptr;
|
|
unsigned long val;
|
|
|
|
val = strtoul(str, &endptr, 10);
|
|
if (endptr == str || val == ULONG_MAX) {
|
|
fprintf(stderr, "xl: failed to convert \"%s\" to number\n", str);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
void replace_string(char **str, const char *val)
|
|
{
|
|
free(*str);
|
|
*str = xstrdup(val);
|
|
}
|
|
|
|
int match_option_size(const char *prefix, size_t len,
|
|
char *arg, char **argopt)
|
|
{
|
|
int rc = strncmp(prefix, arg, len);
|
|
if (!rc) *argopt = arg+len;
|
|
return !rc;
|
|
}
|
|
|
|
/* Parses network data and adds info into nic
|
|
* Returns 1 if the input token does not match one of the keys
|
|
* or parsed values are not correct. Successful parse returns 0 */
|
|
int parse_nic_config(libxl_device_nic *nic, XLU_Config **config, char *token)
|
|
{
|
|
char *endptr, *oparg;
|
|
int i;
|
|
unsigned int val;
|
|
|
|
if (MATCH_OPTION("type", token, oparg)) {
|
|
if (!strcmp("vif", oparg)) {
|
|
nic->nictype = LIBXL_NIC_TYPE_VIF;
|
|
} else if (!strcmp("ioemu", oparg)) {
|
|
nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
|
|
} else {
|
|
fprintf(stderr, "Invalid parameter `type'.\n");
|
|
return 1;
|
|
}
|
|
} else if (MATCH_OPTION("mac", token, oparg)) {
|
|
for (i = 0; i < 6; i++) {
|
|
val = strtoul(oparg, &endptr, 16);
|
|
if ((oparg == endptr) || (val > 255)) {
|
|
fprintf(stderr, "Invalid parameter `mac'.\n");
|
|
return 1;
|
|
}
|
|
nic->mac[i] = val;
|
|
oparg = endptr + 1;
|
|
}
|
|
} else if (MATCH_OPTION("bridge", token, oparg)) {
|
|
replace_string(&nic->bridge, oparg);
|
|
} else if (MATCH_OPTION("netdev", token, oparg)) {
|
|
fprintf(stderr, "the netdev parameter is deprecated, "
|
|
"please use gatewaydev instead\n");
|
|
replace_string(&nic->gatewaydev, oparg);
|
|
} else if (MATCH_OPTION("gatewaydev", token, oparg)) {
|
|
replace_string(&nic->gatewaydev, oparg);
|
|
} else if (MATCH_OPTION("ip", token, oparg)) {
|
|
replace_string(&nic->ip, oparg);
|
|
} else if (MATCH_OPTION("script", token, oparg)) {
|
|
replace_string(&nic->script, oparg);
|
|
} else if (MATCH_OPTION("backend", token, oparg)) {
|
|
replace_string(&nic->backend_domname, oparg);
|
|
} else if (MATCH_OPTION("vifname", token, oparg)) {
|
|
replace_string(&nic->ifname, oparg);
|
|
} else if (MATCH_OPTION("model", token, oparg)) {
|
|
replace_string(&nic->model, oparg);
|
|
} else if (MATCH_OPTION("rate", token, oparg)) {
|
|
parse_vif_rate(config, oparg, nic);
|
|
} else if (MATCH_OPTION("forwarddev", token, oparg)) {
|
|
replace_string(&nic->coloft_forwarddev, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_mirror_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_mirror_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_mirror_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_mirror_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_mirror_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_mirror_port, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_sec_in_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_sec_in_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_sec_in_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_sec_in_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_sec_in_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_sec_in_port, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector0_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector0_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector0_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector0_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector0_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector0_port, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector1_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector1_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector1_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector1_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector1_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector1_port, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector2_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector2_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector2_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector2_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_redirector2_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_redirector2_port, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_pri_in_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_pri_in_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_pri_in_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_pri_in_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_pri_in_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_pri_in_port, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_notify_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_notify_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_notify_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_notify_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_compare_notify_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_compare_notify_port, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_mirror_queue", token, oparg)) {
|
|
replace_string(&nic->colo_filter_mirror_queue, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_mirror_outdev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_mirror_outdev, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_redirector0_queue", token, oparg)) {
|
|
replace_string(&nic->colo_filter_redirector0_queue, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_redirector0_indev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_redirector0_indev, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_redirector0_outdev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_redirector0_outdev, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_redirector1_queue", token, oparg)) {
|
|
replace_string(&nic->colo_filter_redirector1_queue, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_redirector1_indev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_redirector1_indev, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_redirector1_outdev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_redirector1_outdev, oparg);
|
|
} else if (MATCH_OPTION("colo_compare_pri_in", token, oparg)) {
|
|
replace_string(&nic->colo_compare_pri_in, oparg);
|
|
} else if (MATCH_OPTION("colo_compare_sec_in", token, oparg)) {
|
|
replace_string(&nic->colo_compare_sec_in, oparg);
|
|
} else if (MATCH_OPTION("colo_compare_out", token, oparg)) {
|
|
replace_string(&nic->colo_compare_out, oparg);
|
|
} else if (MATCH_OPTION("colo_compare_notify_dev", token, oparg)) {
|
|
replace_string(&nic->colo_compare_notify_dev, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_sec_redirector0_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_sec_redirector0_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_sec_redirector0_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_sec_redirector0_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_sec_redirector0_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_sec_redirector0_port, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_sec_redirector1_id", token, oparg)) {
|
|
replace_string(&nic->colo_sock_sec_redirector1_id, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_sec_redirector1_ip", token, oparg)) {
|
|
replace_string(&nic->colo_sock_sec_redirector1_ip, oparg);
|
|
} else if (MATCH_OPTION("colo_sock_sec_redirector1_port", token, oparg)) {
|
|
replace_string(&nic->colo_sock_sec_redirector1_port, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_sec_redirector0_queue", token, oparg)) {
|
|
replace_string(&nic->colo_filter_sec_redirector0_queue, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_sec_redirector0_indev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_sec_redirector0_indev, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_sec_redirector0_outdev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_sec_redirector0_outdev, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_sec_redirector1_queue", token, oparg)) {
|
|
replace_string(&nic->colo_filter_sec_redirector1_queue, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_sec_redirector1_indev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_sec_redirector1_indev, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_sec_redirector1_outdev", token, oparg)) {
|
|
replace_string(&nic->colo_filter_sec_redirector1_outdev, oparg);
|
|
} else if (MATCH_OPTION("colo_filter_sec_rewriter0_queue", token, oparg)) {
|
|
replace_string(&nic->colo_filter_sec_rewriter0_queue, oparg);
|
|
} else if (MATCH_OPTION("colo_checkpoint_host", token, oparg)) {
|
|
replace_string(&nic->colo_checkpoint_host, oparg);
|
|
} else if (MATCH_OPTION("colo_checkpoint_port", token, oparg)) {
|
|
replace_string(&nic->colo_checkpoint_port, oparg);
|
|
} else if (MATCH_OPTION("accel", token, oparg)) {
|
|
fprintf(stderr, "the accel parameter for vifs is currently not supported\n");
|
|
} else if (MATCH_OPTION("devid", token, oparg)) {
|
|
nic->devid = parse_ulong(oparg);
|
|
} else if (MATCH_OPTION("mtu", token, oparg)) {
|
|
nic->mtu = parse_ulong(oparg);
|
|
} else if (MATCH_OPTION("vlan", token, oparg)) {
|
|
replace_string(&nic->vlan, oparg);
|
|
} else if (!strcmp("trusted", token)) {
|
|
libxl_defbool_set(&nic->trusted, true);
|
|
} else if (!strcmp("untrusted", token)) {
|
|
libxl_defbool_set(&nic->trusted, false);
|
|
} else {
|
|
fprintf(stderr, "unrecognized argument `%s'\n", token);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void parse_vnuma_config(const XLU_Config *config,
|
|
libxl_domain_build_info *b_info)
|
|
{
|
|
libxl_physinfo physinfo;
|
|
uint32_t nr_nodes;
|
|
XLU_ConfigList *vnuma;
|
|
int i, j, len, num_vnuma;
|
|
unsigned long max_vcpus = 0, max_memkb = 0;
|
|
/* Temporary storage for parsed vcpus information to avoid
|
|
* parsing config twice. This array has num_vnuma elements.
|
|
*/
|
|
libxl_bitmap *vcpu_parsed;
|
|
|
|
libxl_physinfo_init(&physinfo);
|
|
if (libxl_get_physinfo(ctx, &physinfo) != 0) {
|
|
libxl_physinfo_dispose(&physinfo);
|
|
fprintf(stderr, "libxl_get_physinfo failed\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
nr_nodes = physinfo.nr_nodes;
|
|
libxl_physinfo_dispose(&physinfo);
|
|
|
|
if (xlu_cfg_get_list(config, "vnuma", &vnuma, &num_vnuma, 1))
|
|
return;
|
|
|
|
if (!num_vnuma)
|
|
return;
|
|
|
|
b_info->num_vnuma_nodes = num_vnuma;
|
|
b_info->vnuma_nodes = xcalloc(num_vnuma, sizeof(libxl_vnode_info));
|
|
vcpu_parsed = xcalloc(num_vnuma, sizeof(libxl_bitmap));
|
|
for (i = 0; i < num_vnuma; i++) {
|
|
libxl_bitmap_init(&vcpu_parsed[i]);
|
|
if (libxl_cpu_bitmap_alloc(ctx, &vcpu_parsed[i], b_info->max_vcpus)) {
|
|
fprintf(stderr, "libxl_node_bitmap_alloc failed.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < b_info->num_vnuma_nodes; i++) {
|
|
libxl_vnode_info *p = &b_info->vnuma_nodes[i];
|
|
|
|
libxl_vnode_info_init(p);
|
|
p->distances = xcalloc(b_info->num_vnuma_nodes,
|
|
sizeof(*p->distances));
|
|
p->num_distances = b_info->num_vnuma_nodes;
|
|
}
|
|
|
|
for (i = 0; i < num_vnuma; i++) {
|
|
XLU_ConfigValue *vnode_spec, *conf_option;
|
|
XLU_ConfigList *vnode_config_list;
|
|
int conf_count;
|
|
libxl_vnode_info *p = &b_info->vnuma_nodes[i];
|
|
|
|
vnode_spec = xlu_cfg_get_listitem2(vnuma, i);
|
|
assert(vnode_spec);
|
|
|
|
xlu_cfg_value_get_list(config, vnode_spec, &vnode_config_list, 0);
|
|
if (!vnode_config_list) {
|
|
fprintf(stderr, "xl: cannot get vnode config option list\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (conf_count = 0;
|
|
(conf_option =
|
|
xlu_cfg_get_listitem2(vnode_config_list, conf_count));
|
|
conf_count++) {
|
|
|
|
if (xlu_cfg_value_type(conf_option) == XLU_STRING) {
|
|
char *buf;
|
|
char *option, *value;
|
|
unsigned long val;
|
|
|
|
xlu_cfg_value_get_string(config, conf_option, &buf, 0);
|
|
|
|
if (!buf) continue;
|
|
|
|
if (split_string_into_pair(buf, '=', &option, &value,
|
|
isspace)) {
|
|
fprintf(stderr, "xl: failed to split \"%s\" into pair\n",
|
|
buf);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!strcmp("pnode", option)) {
|
|
val = parse_ulong(value);
|
|
if (val >= nr_nodes) {
|
|
fprintf(stderr,
|
|
"xl: invalid pnode number: %lu\n", val);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
p->pnode = val;
|
|
libxl_defbool_set(&b_info->numa_placement, false);
|
|
} else if (!strcmp("size", option)) {
|
|
val = parse_ulong(value);
|
|
p->memkb = val << 10;
|
|
max_memkb += p->memkb;
|
|
} else if (!strcmp("vcpus", option)) {
|
|
libxl_string_list cpu_spec_list;
|
|
unsigned long s, e;
|
|
|
|
split_string_into_string_list(value, ",", &cpu_spec_list);
|
|
len = libxl_string_list_length(&cpu_spec_list);
|
|
|
|
for (j = 0; j < len; j++) {
|
|
parse_range(cpu_spec_list[j], &s, &e);
|
|
for (; s <= e; s++) {
|
|
/*
|
|
* Note that if we try to set a bit beyond
|
|
* the size of bitmap, libxl_bitmap_set
|
|
* has no effect. The resulted bitmap
|
|
* doesn't reflect what user wants. The
|
|
* fallout is dealt with later after
|
|
* parsing.
|
|
*/
|
|
libxl_bitmap_set(&vcpu_parsed[i], s);
|
|
max_vcpus++;
|
|
}
|
|
}
|
|
|
|
libxl_string_list_dispose(&cpu_spec_list);
|
|
} else if (!strcmp("vdistances", option)) {
|
|
libxl_string_list vdist;
|
|
|
|
split_string_into_string_list(value, ",", &vdist);
|
|
len = libxl_string_list_length(&vdist);
|
|
|
|
for (j = 0; j < len; j++) {
|
|
val = parse_ulong(vdist[j]);
|
|
p->distances[j] = val;
|
|
}
|
|
libxl_string_list_dispose(&vdist);
|
|
}
|
|
free(option);
|
|
free(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* User has specified maxvcpus= */
|
|
if (b_info->max_vcpus != 0) {
|
|
if (b_info->max_vcpus != max_vcpus) {
|
|
fprintf(stderr, "xl: vnuma vcpus and maxvcpus= mismatch\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else {
|
|
int host_cpus = libxl_get_online_cpus(ctx);
|
|
|
|
if (host_cpus < 0) {
|
|
fprintf(stderr, "Failed to get online cpus\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (host_cpus < max_vcpus) {
|
|
fprintf(stderr, "xl: vnuma specifies more vcpus than pcpus, "\
|
|
"use maxvcpus= to override this check.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
b_info->max_vcpus = max_vcpus;
|
|
}
|
|
|
|
/* User has specified maxmem= */
|
|
if (b_info->max_memkb != LIBXL_MEMKB_DEFAULT &&
|
|
b_info->max_memkb != max_memkb) {
|
|
fprintf(stderr, "xl: maxmem and vnuma memory size mismatch\n");
|
|
exit(EXIT_FAILURE);
|
|
} else
|
|
b_info->max_memkb = max_memkb;
|
|
|
|
for (i = 0; i < b_info->num_vnuma_nodes; i++) {
|
|
libxl_vnode_info *p = &b_info->vnuma_nodes[i];
|
|
|
|
libxl_bitmap_copy_alloc(ctx, &p->vcpus, &vcpu_parsed[i]);
|
|
libxl_bitmap_dispose(&vcpu_parsed[i]);
|
|
}
|
|
|
|
free(vcpu_parsed);
|
|
}
|
|
|
|
/* Parses usbctrl data and adds info into usbctrl
|
|
* Returns 1 if the input token does not match one of the keys
|
|
* or parsed values are not correct. Successful parse returns 0 */
|
|
int parse_usbctrl_config(libxl_device_usbctrl *usbctrl, char *token)
|
|
{
|
|
char *oparg;
|
|
|
|
if (MATCH_OPTION("type", token, oparg)) {
|
|
if (libxl_usbctrl_type_from_string(oparg, &usbctrl->type)) {
|
|
fprintf(stderr, "Invalid usb controller type '%s'\n", oparg);
|
|
return 1;
|
|
}
|
|
} else if (MATCH_OPTION("version", token, oparg)) {
|
|
usbctrl->version = atoi(oparg);
|
|
} else if (MATCH_OPTION("ports", token, oparg)) {
|
|
usbctrl->ports = atoi(oparg);
|
|
} else {
|
|
fprintf(stderr, "Unknown string `%s' in usbctrl spec\n", token);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Parses usbdev data and adds info into usbdev
|
|
* Returns 1 if the input token does not match one of the keys
|
|
* or parsed values are not correct. Successful parse returns 0 */
|
|
int parse_usbdev_config(libxl_device_usbdev *usbdev, char *token)
|
|
{
|
|
char *oparg;
|
|
|
|
if (MATCH_OPTION("type", token, oparg)) {
|
|
if (libxl_usbdev_type_from_string(oparg, &usbdev->type)) {
|
|
fprintf(stderr, "Invalid usb device type: %s\n", optarg);
|
|
return 1;
|
|
}
|
|
} else if (MATCH_OPTION("hostbus", token, oparg)) {
|
|
usbdev->u.hostdev.hostbus = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION("hostaddr", token, oparg)) {
|
|
usbdev->u.hostdev.hostaddr = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION("controller", token, oparg)) {
|
|
usbdev->ctrl = atoi(oparg);
|
|
} else if (MATCH_OPTION("port", token, oparg)) {
|
|
usbdev->port = atoi(oparg);
|
|
} else {
|
|
fprintf(stderr, "Unknown string `%s' in usbdev spec\n", token);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int parse_vdispl_config(libxl_device_vdispl *vdispl, char *token)
|
|
{
|
|
char *oparg;
|
|
libxl_string_list connectors = NULL;
|
|
int i;
|
|
int rc;
|
|
|
|
if (MATCH_OPTION("backend", token, oparg)) {
|
|
vdispl->backend_domname = strdup(oparg);
|
|
} else if (MATCH_OPTION("be-alloc", token, oparg)) {
|
|
vdispl->be_alloc = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION("connectors", token, oparg)) {
|
|
split_string_into_string_list(oparg, ";", &connectors);
|
|
|
|
vdispl->num_connectors = libxl_string_list_length(&connectors);
|
|
vdispl->connectors = xcalloc(vdispl->num_connectors,
|
|
sizeof(*vdispl->connectors));
|
|
|
|
for(i = 0; i < vdispl->num_connectors; i++)
|
|
{
|
|
char *resolution;
|
|
|
|
rc = split_string_into_pair(connectors[i], ':',
|
|
&vdispl->connectors[i].unique_id,
|
|
&resolution, NULL);
|
|
|
|
rc= sscanf(resolution, "%ux%u", &vdispl->connectors[i].width,
|
|
&vdispl->connectors[i].height);
|
|
free(resolution);
|
|
|
|
if (rc != 2) {
|
|
fprintf(stderr, "Can't parse connector resolution\n");
|
|
goto out;
|
|
}
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Unknown string \"%s\" in vdispl spec\n", token);
|
|
rc = 1; goto out;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
libxl_string_list_dispose(&connectors);
|
|
return rc;
|
|
}
|
|
|
|
static int parse_vsnd_params(libxl_vsnd_params *params, char *token)
|
|
{
|
|
char *oparg;
|
|
int i;
|
|
|
|
if (MATCH_OPTION(XENSND_FIELD_SAMPLE_RATES, token, oparg)) {
|
|
libxl_string_list rates = NULL;
|
|
|
|
split_string_into_string_list(oparg, ";", &rates);
|
|
|
|
params->num_sample_rates = libxl_string_list_length(&rates);
|
|
params->sample_rates = xcalloc(params->num_sample_rates,
|
|
sizeof(*params->sample_rates));
|
|
|
|
for (i = 0; i < params->num_sample_rates; i++) {
|
|
params->sample_rates[i] = strtoul(rates[i], NULL, 0);
|
|
}
|
|
|
|
libxl_string_list_dispose(&rates);
|
|
} else if (MATCH_OPTION(XENSND_FIELD_SAMPLE_FORMATS, token, oparg)) {
|
|
libxl_string_list formats = NULL;
|
|
|
|
split_string_into_string_list(oparg, ";", &formats);
|
|
|
|
params->num_sample_formats = libxl_string_list_length(&formats);
|
|
params->sample_formats = xcalloc(params->num_sample_formats,
|
|
sizeof(*params->sample_formats));
|
|
|
|
for (i = 0; i < params->num_sample_formats; i++) {
|
|
libxl_vsnd_pcm_format format;
|
|
|
|
if (libxl_vsnd_pcm_format_from_string(formats[i], &format)) {
|
|
fprintf(stderr, "Invalid pcm format: %s\n", formats[i]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
params->sample_formats[i] = format;
|
|
}
|
|
|
|
libxl_string_list_dispose(&formats);
|
|
} else if (MATCH_OPTION(XENSND_FIELD_CHANNELS_MIN, token, oparg)) {
|
|
params->channels_min = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENSND_FIELD_CHANNELS_MAX, token, oparg)) {
|
|
params->channels_max = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENSND_FIELD_BUFFER_SIZE, token, oparg)) {
|
|
params->buffer_size = strtoul(oparg, NULL, 0);
|
|
} else {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_vsnd_pcm_stream(libxl_device_vsnd *vsnd, char *param)
|
|
{
|
|
if (vsnd->num_vsnd_pcms == 0) {
|
|
fprintf(stderr, "No vsnd pcm device\n");
|
|
return -1;
|
|
}
|
|
|
|
libxl_vsnd_pcm *pcm = &vsnd->pcms[vsnd->num_vsnd_pcms - 1];
|
|
|
|
if (pcm->num_vsnd_streams == 0) {
|
|
fprintf(stderr, "No vsnd stream\n");
|
|
return -1;
|
|
}
|
|
|
|
libxl_vsnd_stream *stream = &pcm->streams[pcm->num_vsnd_streams - 1];
|
|
|
|
if (parse_vsnd_params(&stream->params, param)) {
|
|
char *oparg;
|
|
|
|
if (MATCH_OPTION(XENSND_FIELD_STREAM_UNIQUE_ID, param, oparg)) {
|
|
stream->unique_id = strdup(oparg);
|
|
} else if (MATCH_OPTION(XENSND_FIELD_TYPE, param, oparg)) {
|
|
if (libxl_vsnd_stream_type_from_string(oparg, &stream->type)) {
|
|
fprintf(stderr, "Invalid stream type: %s\n", oparg);
|
|
return -1;
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Invalid parameter: %s\n", param);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_vsnd_pcm_param(libxl_device_vsnd *vsnd, char *param)
|
|
{
|
|
if (vsnd->num_vsnd_pcms == 0) {
|
|
fprintf(stderr, "No pcm device\n");
|
|
return -1;
|
|
}
|
|
|
|
libxl_vsnd_pcm *pcm = &vsnd->pcms[vsnd->num_vsnd_pcms - 1];
|
|
|
|
if (parse_vsnd_params(&pcm->params, param)) {
|
|
char *oparg;
|
|
|
|
if (MATCH_OPTION(XENSND_FIELD_DEVICE_NAME, param, oparg)) {
|
|
pcm->name = strdup(oparg);
|
|
} else {
|
|
fprintf(stderr, "Invalid parameter: %s\n", param);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_vsnd_card_param(libxl_device_vsnd *vsnd, char *param)
|
|
{
|
|
if (parse_vsnd_params(&vsnd->params, param)) {
|
|
char *oparg;
|
|
|
|
if (MATCH_OPTION("backend", param, oparg)) {
|
|
vsnd->backend_domname = strdup(oparg);
|
|
} else if (MATCH_OPTION(XENSND_FIELD_VCARD_SHORT_NAME, param, oparg)) {
|
|
vsnd->short_name = strdup(oparg);
|
|
} else if (MATCH_OPTION(XENSND_FIELD_VCARD_LONG_NAME, param, oparg)) {
|
|
vsnd->long_name = strdup(oparg);
|
|
} else {
|
|
fprintf(stderr, "Invalid parameter: %s\n", param);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_vsnd_create_item(libxl_device_vsnd *vsnd, const char *key)
|
|
{
|
|
if (strcasecmp(key, "card") == 0) {
|
|
|
|
} else if (strcasecmp(key, "pcm") == 0) {
|
|
ARRAY_EXTEND_INIT_NODEVID(vsnd->pcms, vsnd->num_vsnd_pcms,
|
|
libxl_vsnd_pcm_init);
|
|
} else if (strcasecmp(key, "stream") == 0) {
|
|
if (vsnd->num_vsnd_pcms == 0) {
|
|
ARRAY_EXTEND_INIT_NODEVID(vsnd->pcms, vsnd->num_vsnd_pcms,
|
|
libxl_vsnd_pcm_init);
|
|
}
|
|
|
|
libxl_vsnd_pcm *pcm = &vsnd->pcms[vsnd->num_vsnd_pcms - 1];
|
|
|
|
ARRAY_EXTEND_INIT_NODEVID(pcm->streams, pcm->num_vsnd_streams,
|
|
libxl_vsnd_stream_init);
|
|
} else {
|
|
fprintf(stderr, "Invalid key: %s\n", key);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int parse_vsnd_item(libxl_device_vsnd *vsnd, const char *spec)
|
|
{
|
|
char *buf = strdup(spec);
|
|
char *token = strtok(buf, ",");
|
|
char *key = NULL;
|
|
int ret;
|
|
|
|
while (token) {
|
|
while (*token == ' ') token++;
|
|
|
|
if (!key) {
|
|
key = token;
|
|
ret = parse_vsnd_create_item(vsnd, key);
|
|
if (ret) goto out;
|
|
} else {
|
|
if (strcasecmp(key, "card") == 0) {
|
|
ret = parse_vsnd_card_param(vsnd, token);
|
|
if (ret) goto out;
|
|
} else if (strcasecmp(key, "pcm") == 0) {
|
|
ret = parse_vsnd_pcm_param(vsnd, token);
|
|
if (ret) goto out;
|
|
} else if (strcasecmp(key, "stream") == 0) {
|
|
ret = parse_vsnd_pcm_stream(vsnd, token);
|
|
if (ret) goto out;
|
|
}
|
|
}
|
|
token = strtok (NULL, ",");
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
static void parse_vsnd_card_config(const XLU_Config *config,
|
|
XLU_ConfigValue *card_value,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
int ret;
|
|
XLU_ConfigList *card_list;
|
|
libxl_device_vsnd *vsnd;
|
|
const char *card_item;
|
|
int item = 0;
|
|
|
|
ret = xlu_cfg_value_get_list(config, card_value, &card_list, 0);
|
|
|
|
if (ret) {
|
|
fprintf(stderr, "Failed to get vsnd card list: %s\n", strerror(ret));
|
|
goto out;
|
|
}
|
|
|
|
vsnd = ARRAY_EXTEND_INIT(d_config->vsnds,
|
|
d_config->num_vsnds,
|
|
libxl_device_vsnd_init);
|
|
|
|
while ((card_item = xlu_cfg_get_listitem(card_list, item++)) != NULL) {
|
|
ret = parse_vsnd_item(vsnd, card_item);
|
|
if (ret) goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
if (ret) exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static void parse_vsnd_config(const XLU_Config *config,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
XLU_ConfigList *vsnds;
|
|
|
|
if (!xlu_cfg_get_list(config, "vsnd", &vsnds, 0, 0)) {
|
|
XLU_ConfigValue *card_value;
|
|
|
|
d_config->num_vsnds = 0;
|
|
d_config->vsnds = NULL;
|
|
|
|
while ((card_value = xlu_cfg_get_listitem2(vsnds, d_config->num_vsnds))
|
|
!= NULL) {
|
|
parse_vsnd_card_config(config, card_value, d_config);
|
|
}
|
|
}
|
|
}
|
|
|
|
int parse_vkb_config(libxl_device_vkb *vkb, char *token)
|
|
{
|
|
char *oparg;
|
|
|
|
if (MATCH_OPTION("backend", token, oparg)) {
|
|
vkb->backend_domname = strdup(oparg);
|
|
} else if (MATCH_OPTION("backend-type", token, oparg)) {
|
|
libxl_vkb_backend backend_type;
|
|
if (libxl_vkb_backend_from_string(oparg, &backend_type)) {
|
|
fprintf(stderr, "Unknown backend_type \"%s\" in vkb spec\n",
|
|
oparg);
|
|
return -1;
|
|
}
|
|
vkb->backend_type = backend_type;
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_UNIQUE_ID, token, oparg)) {
|
|
vkb->unique_id = strdup(oparg);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_FEAT_DSBL_KEYBRD, token, oparg)) {
|
|
vkb->feature_disable_keyboard = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_FEAT_DSBL_POINTER, token, oparg)) {
|
|
vkb->feature_disable_pointer = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_FEAT_ABS_POINTER, token, oparg)) {
|
|
vkb->feature_abs_pointer = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_FEAT_RAW_POINTER, token, oparg)) {
|
|
vkb->feature_raw_pointer = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_FEAT_MTOUCH, token, oparg)) {
|
|
vkb->feature_multi_touch = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_MT_WIDTH, token, oparg)) {
|
|
vkb->multi_touch_width = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_MT_HEIGHT, token, oparg)) {
|
|
vkb->multi_touch_height = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_MT_NUM_CONTACTS, token, oparg)) {
|
|
vkb->multi_touch_num_contacts = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_WIDTH, token, oparg)) {
|
|
vkb->width = strtoul(oparg, NULL, 0);
|
|
} else if (MATCH_OPTION(XENKBD_FIELD_HEIGHT, token, oparg)) {
|
|
vkb->height = strtoul(oparg, NULL, 0);
|
|
} else {
|
|
fprintf(stderr, "Unknown string \"%s\" in vkb spec\n", token);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void parse_vkb_list(const XLU_Config *config,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
XLU_ConfigList *vkbs;
|
|
const char *item;
|
|
char *buf = NULL;
|
|
int rc;
|
|
|
|
if (!xlu_cfg_get_list (config, "vkb", &vkbs, 0, 0)) {
|
|
int entry = 0;
|
|
while ((item = xlu_cfg_get_listitem(vkbs, entry)) != NULL) {
|
|
libxl_device_vkb *vkb;
|
|
char *p;
|
|
|
|
vkb = ARRAY_EXTEND_INIT(d_config->vkbs,
|
|
d_config->num_vkbs,
|
|
libxl_device_vkb_init);
|
|
|
|
buf = strdup(item);
|
|
|
|
p = strtok (buf, ",");
|
|
while (p != NULL)
|
|
{
|
|
while (*p == ' ') p++;
|
|
|
|
rc = parse_vkb_config(vkb, p);
|
|
if (rc) goto out;
|
|
|
|
p = strtok (NULL, ",");
|
|
}
|
|
|
|
if (vkb->backend_type == LIBXL_VKB_BACKEND_UNKNOWN) {
|
|
fprintf(stderr, "backend-type should be set in vkb spec\n");
|
|
rc = ERROR_FAIL; goto out;
|
|
}
|
|
|
|
if (vkb->multi_touch_height || vkb->multi_touch_width ||
|
|
vkb->multi_touch_num_contacts) {
|
|
vkb->feature_multi_touch = true;
|
|
}
|
|
|
|
if (vkb->feature_multi_touch && !(vkb->multi_touch_height ||
|
|
vkb->multi_touch_width || vkb->multi_touch_num_contacts)) {
|
|
fprintf(stderr, XENKBD_FIELD_MT_WIDTH", "XENKBD_FIELD_MT_HEIGHT", "
|
|
XENKBD_FIELD_MT_NUM_CONTACTS" should be set for "
|
|
"multi touch in vkb spec\n");
|
|
rc = ERROR_FAIL; goto out;
|
|
}
|
|
|
|
entry++;
|
|
}
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
free(buf);
|
|
if (rc) exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static int parse_virtio_config(libxl_device_virtio *virtio, char *token)
|
|
{
|
|
char *oparg;
|
|
int rc;
|
|
|
|
if (MATCH_OPTION("backend", token, oparg)) {
|
|
virtio->backend_domname = strdup(oparg);
|
|
} else if (MATCH_OPTION("type", token, oparg)) {
|
|
virtio->type = strdup(oparg);
|
|
} else if (MATCH_OPTION("transport", token, oparg)) {
|
|
rc = libxl_virtio_transport_from_string(oparg, &virtio->transport);
|
|
if (rc) return rc;
|
|
} else if (MATCH_OPTION("grant_usage", token, oparg)) {
|
|
libxl_defbool_set(&virtio->grant_usage, strtoul(oparg, NULL, 0));
|
|
} else {
|
|
fprintf(stderr, "Unknown string \"%s\" in virtio spec\n", token);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void parse_virtio_list(const XLU_Config *config,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
XLU_ConfigList *virtios;
|
|
const char *item;
|
|
char *buf = NULL, *oparg, *str = NULL;
|
|
int rc;
|
|
|
|
if (!xlu_cfg_get_list (config, "virtio", &virtios, 0, 0)) {
|
|
int entry = 0;
|
|
while ((item = xlu_cfg_get_listitem(virtios, entry)) != NULL) {
|
|
libxl_device_virtio *virtio;
|
|
char *p;
|
|
|
|
virtio = ARRAY_EXTEND_INIT(d_config->virtios, d_config->num_virtios,
|
|
libxl_device_virtio_init);
|
|
|
|
buf = strdup(item);
|
|
|
|
p = strtok(buf, ",");
|
|
while (p != NULL)
|
|
{
|
|
while (*p == ' ') p++;
|
|
|
|
// Type may contain a comma, do special handling.
|
|
if (MATCH_OPTION("type", p, oparg)) {
|
|
if (!strncmp(oparg, "virtio", strlen("virtio"))) {
|
|
char *p2 = strtok(NULL, ",");
|
|
str = malloc(strlen(p) + strlen(p2) + 2);
|
|
|
|
strcpy(str, p);
|
|
strcat(str, ",");
|
|
strcat(str, p2);
|
|
p = str;
|
|
}
|
|
}
|
|
|
|
rc = parse_virtio_config(virtio, p);
|
|
if (rc) goto out;
|
|
|
|
free(str);
|
|
str = NULL;
|
|
p = strtok(NULL, ",");
|
|
}
|
|
|
|
entry++;
|
|
free(buf);
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
out:
|
|
free(buf);
|
|
if (rc) exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static int parse_arm_sci_config(XLU_Config *cfg, libxl_arm_sci *arm_sci,
|
|
const char *str)
|
|
{
|
|
int ret = 0;
|
|
char *buf2, *ptr;
|
|
char *oparg;
|
|
|
|
if (NULL == (buf2 = ptr = strdup(str)))
|
|
return ERROR_NOMEM;
|
|
|
|
ptr = strtok(buf2, ",");
|
|
while (ptr != NULL)
|
|
{
|
|
if (MATCH_OPTION("type", ptr, oparg)) {
|
|
ret = libxl_arm_sci_type_from_string(oparg, &arm_sci->type);
|
|
if (ret) {
|
|
fprintf(stderr, "Unknown ARM_SCI type: %s\n", oparg);
|
|
ret = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ptr = strtok(NULL, ",");
|
|
}
|
|
|
|
out:
|
|
free(buf2);
|
|
return ret;
|
|
}
|
|
|
|
int parse_xsquota_item(const char *buf, struct libxl_xs_quota_item *item)
|
|
{
|
|
const char *eq;
|
|
char *endptr;
|
|
unsigned long val;
|
|
|
|
eq = strchr(buf, '=');
|
|
if (!eq) {
|
|
fprintf(stderr, "Quota specification \"%s\" lacks \"=\".\n", buf);
|
|
return ERROR_INVAL;
|
|
}
|
|
errno = 0;
|
|
item->name = strndup(buf, eq - buf);
|
|
if (!item->name)
|
|
return ERROR_NOMEM;
|
|
val = strtoul(eq + 1, &endptr, 0);
|
|
if (errno || !eq[1] || *endptr || (unsigned int)val != val) {
|
|
fprintf(stderr,
|
|
"Quota specification \"%s\" uses illegal value \"%s\".\n",
|
|
buf, eq + 1);
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
item->val = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void parse_config_data(const char *config_source,
|
|
const char *config_data,
|
|
int config_len,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
libxl_physinfo physinfo;
|
|
const char *buf;
|
|
long l, vcpus = 0;
|
|
XLU_Config *config;
|
|
XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms,
|
|
*usbctrls, *usbdevs, *p9devs, *vdispls, *pvcallsifs_devs;
|
|
XLU_ConfigList *channels, *ioports, *irqs, *iomem, *viridian, *dtdevs,
|
|
*mca_caps, *smbios, *llc_colors, *xs_quota;
|
|
int num_ioports, num_irqs, num_iomem, num_cpus, num_viridian, num_mca_caps;
|
|
int num_smbios;
|
|
int pci_power_mgmt = 0;
|
|
int pci_msitranslate = 0;
|
|
int pci_permissive = 0;
|
|
int pci_seize = 0;
|
|
int i, e;
|
|
int num_llc_colors;
|
|
int num_xs_quota;
|
|
char *kernel_basename;
|
|
|
|
libxl_domain_create_info *c_info = &d_config->c_info;
|
|
libxl_domain_build_info *b_info = &d_config->b_info;
|
|
|
|
libxl_physinfo_init(&physinfo);
|
|
if (libxl_get_physinfo(ctx, &physinfo) != 0) {
|
|
libxl_physinfo_dispose(&physinfo);
|
|
fprintf(stderr, "libxl_get_physinfo failed\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
libxl_physinfo_dispose(&physinfo);
|
|
|
|
config= xlu_cfg_init(stderr, config_source);
|
|
if (!config) {
|
|
fprintf(stderr, "Failed to allocate for configuration\n");
|
|
exit(1);
|
|
}
|
|
|
|
e= xlu_cfg_readdata(config, config_data, config_len);
|
|
if (e) {
|
|
fprintf(stderr, "Failed to parse config: %s\n", strerror(e));
|
|
exit(1);
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "init_seclabel", &buf, 0))
|
|
xlu_cfg_replace_string(config, "init_seclabel",
|
|
&c_info->ssid_label, 0);
|
|
|
|
if (!xlu_cfg_get_string (config, "seclabel", &buf, 0)) {
|
|
if (c_info->ssid_label)
|
|
xlu_cfg_replace_string(config, "seclabel",
|
|
&b_info->exec_ssid_label, 0);
|
|
else
|
|
xlu_cfg_replace_string(config, "seclabel",
|
|
&c_info->ssid_label, 0);
|
|
}
|
|
|
|
libxl_defbool_set(&c_info->run_hotplug_scripts, run_hotplug_scripts);
|
|
|
|
if (!xlu_cfg_get_string(config, "type", &buf, 0)) {
|
|
if (!strncmp(buf, "hvm", strlen(buf)))
|
|
c_info->type = LIBXL_DOMAIN_TYPE_HVM;
|
|
else if (!strncmp(buf, "pv", strlen(buf)))
|
|
c_info->type = LIBXL_DOMAIN_TYPE_PV;
|
|
else if (!strncmp(buf, "pvh", strlen(buf)))
|
|
c_info->type = LIBXL_DOMAIN_TYPE_PVH;
|
|
else {
|
|
fprintf(stderr, "Invalid domain type %s.\n", buf);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* Deprecated since Xen 4.10. */
|
|
if (!xlu_cfg_get_string(config, "builder", &buf, 0)) {
|
|
libxl_domain_type builder_type;
|
|
|
|
if (!strncmp(buf, "hvm", strlen(buf)))
|
|
builder_type = LIBXL_DOMAIN_TYPE_HVM;
|
|
else if (!strncmp(buf, "generic", strlen(buf)))
|
|
builder_type = LIBXL_DOMAIN_TYPE_PV;
|
|
else {
|
|
fprintf(stderr, "Invalid domain type %s.\n", buf);
|
|
exit(1);
|
|
}
|
|
|
|
if (c_info->type != LIBXL_DOMAIN_TYPE_INVALID &&
|
|
c_info->type != builder_type) {
|
|
fprintf(stderr,
|
|
"Contradicting \"builder\" and \"type\" options specified.\n");
|
|
exit(1);
|
|
}
|
|
c_info->type = builder_type;
|
|
}
|
|
|
|
if (c_info->type == LIBXL_DOMAIN_TYPE_INVALID)
|
|
#if defined(__arm__) || defined(__aarch64__)
|
|
c_info->type = LIBXL_DOMAIN_TYPE_PVH;
|
|
#else
|
|
c_info->type = LIBXL_DOMAIN_TYPE_PV;
|
|
#endif
|
|
|
|
xlu_cfg_get_defbool(config, "hap", &c_info->hap, 0);
|
|
|
|
if (xlu_cfg_replace_string (config, "name", &c_info->name, 0)) {
|
|
fprintf(stderr, "Domain name must be specified.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "uuid", &buf, 0) ) {
|
|
if ( libxl_uuid_from_string(&c_info->uuid, buf) ) {
|
|
fprintf(stderr, "Failed to parse UUID: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
}else{
|
|
libxl_uuid_generate(&c_info->uuid);
|
|
}
|
|
|
|
xlu_cfg_get_defbool(config, "oos", &c_info->oos, 0);
|
|
|
|
if (!xlu_cfg_get_string (config, "pool", &buf, 0))
|
|
xlu_cfg_replace_string(config, "pool", &c_info->pool_name, 0);
|
|
|
|
if (!xlu_cfg_get_long (config, "xenstore_feature_mask", &l, 0))
|
|
b_info->xenstore_feature_mask = l;
|
|
|
|
if (!xlu_cfg_get_list(config, "xenstore_quota", &xs_quota, &num_xs_quota, 0)) {
|
|
b_info->xenstore_quota.num_quota = num_xs_quota;
|
|
b_info->xenstore_quota.quota = xcalloc(num_xs_quota, sizeof(* b_info->xenstore_quota.quota));
|
|
|
|
for (i = 0; i < num_xs_quota; i++) {
|
|
buf = xlu_cfg_get_listitem(xs_quota, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Can't get element %d in Xenstore quota list\n", i);
|
|
exit(1);
|
|
}
|
|
if (parse_xsquota_item(buf, b_info->xenstore_quota.quota + i))
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
libxl_domain_build_info_init_type(b_info, c_info->type);
|
|
|
|
if (b_info->type == LIBXL_DOMAIN_TYPE_PVH) {
|
|
xlu_cfg_get_defbool(config, "pvshim", &b_info->u.pvh.pvshim, 0);
|
|
if (!xlu_cfg_get_string(config, "pvshim_path", &buf, 0))
|
|
xlu_cfg_replace_string(config, "pvshim_path",
|
|
&b_info->u.pvh.pvshim_path, 0);
|
|
if (!xlu_cfg_get_string(config, "pvshim_cmdline", &buf, 0))
|
|
xlu_cfg_replace_string(config, "pvshim_cmdline",
|
|
&b_info->u.pvh.pvshim_cmdline, 0);
|
|
if (!xlu_cfg_get_string(config, "pvshim_extra", &buf, 0))
|
|
xlu_cfg_replace_string(config, "pvshim_extra",
|
|
&b_info->u.pvh.pvshim_extra, 0);
|
|
}
|
|
|
|
if (blkdev_start)
|
|
b_info->blkdev_start = strdup(blkdev_start);
|
|
|
|
/* the following is the actual config parsing with overriding
|
|
* values in the structures */
|
|
if (!xlu_cfg_get_long (config, "cpu_weight", &l, 0))
|
|
b_info->sched_params.weight = l;
|
|
if (!xlu_cfg_get_long (config, "cap", &l, 0))
|
|
b_info->sched_params.cap = l;
|
|
if (!xlu_cfg_get_long (config, "period", &l, 0))
|
|
b_info->sched_params.period = l;
|
|
if (!xlu_cfg_get_long (config, "slice", &l, 0))
|
|
b_info->sched_params.slice = l;
|
|
if (!xlu_cfg_get_long (config, "latency", &l, 0))
|
|
b_info->sched_params.latency = l;
|
|
if (!xlu_cfg_get_long (config, "extratime", &l, 0))
|
|
b_info->sched_params.extratime = l;
|
|
|
|
if (!xlu_cfg_get_long (config, "memory", &l, 0))
|
|
b_info->target_memkb = l * 1024;
|
|
|
|
if (!xlu_cfg_get_long (config, "maxmem", &l, 0))
|
|
b_info->max_memkb = l * 1024;
|
|
|
|
if (!xlu_cfg_get_list(config, "llc_colors", &llc_colors, &num_llc_colors, 0)) {
|
|
int cur_index = 0;
|
|
|
|
b_info->num_llc_colors = 0;
|
|
for (i = 0; i < num_llc_colors; i++) {
|
|
uint32_t start = 0, end = 0, k;
|
|
|
|
buf = xlu_cfg_get_listitem(llc_colors, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Can't get element %d in LLC color list\n", i);
|
|
exit(1);
|
|
}
|
|
|
|
if (sscanf(buf, "%" SCNu32 "-%" SCNu32, &start, &end) != 2) {
|
|
if (sscanf(buf, "%" SCNu32, &start) != 1) {
|
|
fprintf(stderr, "xl: Invalid LLC color range: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
end = start;
|
|
} else if (start > end) {
|
|
fprintf(stderr,
|
|
"xl: Start LLC color is greater than end: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
|
|
b_info->num_llc_colors += (end - start) + 1;
|
|
b_info->llc_colors = (uint32_t *)realloc(b_info->llc_colors,
|
|
sizeof(*b_info->llc_colors) * b_info->num_llc_colors);
|
|
|
|
for (k = start; k <= end; k++)
|
|
b_info->llc_colors[cur_index++] = k;
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_long (config, "vcpus", &l, 0)) {
|
|
vcpus = l;
|
|
}
|
|
if (vcpus < 1) {
|
|
/*
|
|
* Default to 1 vCPU, libxl is already assuming this
|
|
* when vcpus == 0 and parse_vcpu_affinity() also assume there's at
|
|
* least one vcpu.
|
|
*/
|
|
vcpus = 1;
|
|
}
|
|
if (libxl_cpu_bitmap_alloc(ctx, &b_info->avail_vcpus, vcpus)) {
|
|
fprintf(stderr, "Unable to allocate cpumap\n");
|
|
exit(1);
|
|
}
|
|
libxl_bitmap_set_none(&b_info->avail_vcpus);
|
|
for (long vcpu = vcpus; vcpu-- > 0;)
|
|
libxl_bitmap_set((&b_info->avail_vcpus), vcpu);
|
|
|
|
if (!xlu_cfg_get_long (config, "maxvcpus", &l, 0))
|
|
b_info->max_vcpus = l;
|
|
|
|
if (!xlu_cfg_get_string(config, "vuart", &buf, 0)) {
|
|
if (libxl_vuart_type_from_string(buf, &b_info->arch_arm.vuart)) {
|
|
fprintf(stderr, "ERROR: invalid value \"%s\" for \"vuart\"\n",
|
|
buf);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
parse_vnuma_config(config, b_info);
|
|
|
|
/* Set max_memkb to target_memkb and max_vcpus to avail_vcpus if
|
|
* they are not set by user specified config option or vnuma.
|
|
*/
|
|
if (b_info->max_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->max_memkb = b_info->target_memkb;
|
|
if (b_info->max_vcpus == 0)
|
|
b_info->max_vcpus = vcpus;
|
|
|
|
if (b_info->max_vcpus < vcpus) {
|
|
fprintf(stderr, "xl: maxvcpus < vcpus\n");
|
|
exit(1);
|
|
}
|
|
|
|
buf = NULL;
|
|
if (!xlu_cfg_get_list (config, "cpus", &cpus, &num_cpus, 1) ||
|
|
!xlu_cfg_get_string (config, "cpus", &buf, 0))
|
|
parse_vcpu_affinity(b_info, cpus, buf, num_cpus, /* is_hard */ true);
|
|
|
|
buf = NULL;
|
|
if (!xlu_cfg_get_list (config, "cpus_soft", &cpus, &num_cpus, 1) ||
|
|
!xlu_cfg_get_string (config, "cpus_soft", &buf, 0))
|
|
parse_vcpu_affinity(b_info, cpus, buf, num_cpus, false);
|
|
|
|
e = xlu_cfg_get_bounded_long (config, "max_grant_frames", 0, INT_MAX,
|
|
&l, 1);
|
|
if (e == ESRCH) /* not specified */
|
|
b_info->max_grant_frames = max_grant_frames;
|
|
else if (!e)
|
|
b_info->max_grant_frames = l;
|
|
else
|
|
exit(1);
|
|
|
|
e = xlu_cfg_get_bounded_long (config, "max_maptrack_frames", 0,
|
|
INT_MAX, &l, 1);
|
|
if (e == ESRCH) /* not specified */
|
|
b_info->max_maptrack_frames = max_maptrack_frames;
|
|
else if (!e)
|
|
b_info->max_maptrack_frames = l;
|
|
else
|
|
exit(1);
|
|
|
|
e = xlu_cfg_get_bounded_long (config, "max_grant_version", 0,
|
|
INT_MAX, &l, 1);
|
|
if (e == ESRCH) /* not specified */
|
|
b_info->max_grant_version = max_grant_version;
|
|
else if (!e)
|
|
b_info->max_grant_version = l;
|
|
else
|
|
exit(1);
|
|
|
|
libxl_defbool_set(&b_info->claim_mode, claim_mode);
|
|
|
|
if (xlu_cfg_get_string (config, "on_poweroff", &buf, 0))
|
|
buf = "destroy";
|
|
if (!parse_action_on_shutdown(buf, &d_config->on_poweroff)) {
|
|
fprintf(stderr, "Unknown on_poweroff action \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
|
|
if (xlu_cfg_get_string (config, "on_reboot", &buf, 0))
|
|
buf = "restart";
|
|
if (!parse_action_on_shutdown(buf, &d_config->on_reboot)) {
|
|
fprintf(stderr, "Unknown on_reboot action \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
|
|
if (xlu_cfg_get_string (config, "on_watchdog", &buf, 0))
|
|
buf = "destroy";
|
|
if (!parse_action_on_shutdown(buf, &d_config->on_watchdog)) {
|
|
fprintf(stderr, "Unknown on_watchdog action \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
if (xlu_cfg_get_string (config, "on_crash", &buf, 0))
|
|
buf = "destroy";
|
|
if (!parse_action_on_shutdown(buf, &d_config->on_crash)) {
|
|
fprintf(stderr, "Unknown on_crash action \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
|
|
if (xlu_cfg_get_string (config, "on_soft_reset", &buf, 0))
|
|
buf = "soft-reset";
|
|
if (!parse_action_on_shutdown(buf, &d_config->on_soft_reset)) {
|
|
fprintf(stderr, "Unknown on_soft_reset action \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
|
|
/* To be reworked (automatically enabled) once the auto ballooning
|
|
* after guest starts is done (with PCI devices passed in). */
|
|
if (c_info->type == LIBXL_DOMAIN_TYPE_PV) {
|
|
xlu_cfg_get_defbool(config, "e820_host", &b_info->u.pv.e820_host, 0);
|
|
}
|
|
|
|
if (!xlu_cfg_get_long (config, "pci_msitranslate", &l, 0))
|
|
pci_msitranslate = l;
|
|
|
|
if (!xlu_cfg_get_long (config, "pci_power_mgmt", &l, 0))
|
|
pci_power_mgmt = l;
|
|
|
|
if (!xlu_cfg_get_long (config, "pci_permissive", &l, 0))
|
|
pci_permissive = l;
|
|
|
|
if (!xlu_cfg_get_long (config, "pci_seize", &l, 0))
|
|
pci_seize = l;
|
|
|
|
if (!xlu_cfg_get_string(config, "rdm", &buf, 0)) {
|
|
libxl_rdm_reserve rdm;
|
|
if (!xlu_rdm_parse(config, &rdm, buf)) {
|
|
b_info->u.hvm.rdm.strategy = rdm.strategy;
|
|
b_info->u.hvm.rdm.policy = rdm.policy;
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list (config, "pci", &pcis, 0, 0)) {
|
|
d_config->num_pcidevs = 0;
|
|
d_config->pcidevs = NULL;
|
|
for(i = 0; (buf = xlu_cfg_get_listitem (pcis, i)) != NULL; i++) {
|
|
libxl_device_pci *pci;
|
|
|
|
pci = ARRAY_EXTEND_INIT_NODEVID(d_config->pcidevs,
|
|
d_config->num_pcidevs,
|
|
libxl_device_pci_init);
|
|
pci->msitranslate = pci_msitranslate;
|
|
pci->power_mgmt = pci_power_mgmt;
|
|
pci->permissive = pci_permissive;
|
|
pci->seize = pci_seize;
|
|
/*
|
|
* Like other pci option, the per-device policy always follows
|
|
* the global policy by default.
|
|
*/
|
|
pci->rdm_policy = b_info->u.hvm.rdm.policy;
|
|
e = xlu_pci_parse_spec_string(config, pci, buf);
|
|
if (e) {
|
|
fprintf(stderr,
|
|
"unable to parse PCI_SPEC_STRING `%s' for passthrough\n",
|
|
buf);
|
|
exit(-e);
|
|
}
|
|
}
|
|
if (d_config->num_pcidevs && c_info->type == LIBXL_DOMAIN_TYPE_PV)
|
|
libxl_defbool_set(&b_info->u.pv.e820_host, true);
|
|
}
|
|
|
|
if (!xlu_cfg_get_list (config, "dtdev", &dtdevs, 0, 0)) {
|
|
d_config->num_dtdevs = 0;
|
|
d_config->dtdevs = NULL;
|
|
for (i = 0; (buf = xlu_cfg_get_listitem(dtdevs, i)) != NULL; i++) {
|
|
libxl_device_dtdev *dtdev;
|
|
|
|
dtdev = ARRAY_EXTEND_INIT_NODEVID(d_config->dtdevs,
|
|
d_config->num_dtdevs,
|
|
libxl_device_dtdev_init);
|
|
|
|
dtdev->path = strdup(buf);
|
|
if (dtdev->path == NULL) {
|
|
fprintf(stderr, "unable to duplicate string for dtdevs\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_string(config, "passthrough", &buf, 0)) {
|
|
if (libxl_passthrough_from_string(buf, &c_info->passthrough)) {
|
|
fprintf(stderr,
|
|
"ERROR: unknown passthrough option '%s'\n",
|
|
buf);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_long(config, "shadow_memory", &l, 0))
|
|
b_info->shadow_memkb = l * 1024;
|
|
|
|
xlu_cfg_get_defbool(config, "nomigrate", &b_info->disable_migrate, 0);
|
|
|
|
if (!xlu_cfg_get_long(config, "tsc_mode", &l, 1)) {
|
|
const char *s = libxl_tsc_mode_to_string(l);
|
|
fprintf(stderr, "WARNING: specifying \"tsc_mode\" as an integer is deprecated. "
|
|
"Please use the named parameter variant. %s%s%s\n",
|
|
s ? "e.g. tsc_mode=\"" : "",
|
|
s ? s : "",
|
|
s ? "\"" : "");
|
|
|
|
if (l < LIBXL_TSC_MODE_DEFAULT ||
|
|
l > LIBXL_TSC_MODE_NATIVE_PARAVIRT) {
|
|
fprintf(stderr, "ERROR: invalid value %ld for \"tsc_mode\"\n", l);
|
|
exit (1);
|
|
}
|
|
b_info->tsc_mode = l;
|
|
} else if (!xlu_cfg_get_string(config, "tsc_mode", &buf, 0)) {
|
|
if (libxl_tsc_mode_from_string(buf, &b_info->tsc_mode)) {
|
|
fprintf(stderr, "ERROR: invalid value \"%s\" for \"tsc_mode\"\n",
|
|
buf);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_long(config, "rtc_timeoffset", &l, 0))
|
|
b_info->rtc_timeoffset = l;
|
|
|
|
if (!xlu_cfg_get_long(config, "vncviewer", &l, 0))
|
|
fprintf(stderr, "WARNING: ignoring \"vncviewer\" option. "
|
|
"Use \"-V\" option of \"xl create\" to automatically spawn vncviewer.\n");
|
|
|
|
xlu_cfg_get_defbool(config, "localtime", &b_info->localtime, 0);
|
|
|
|
if (!xlu_cfg_get_long (config, "videoram", &l, 0))
|
|
b_info->video_memkb = l * 1024;
|
|
|
|
if (!xlu_cfg_get_long(config, "max_event_channels", &l, 0))
|
|
b_info->event_channels = l;
|
|
|
|
xlu_cfg_replace_string (config, "kernel", &b_info->kernel, 0);
|
|
xlu_cfg_replace_string (config, "ramdisk", &b_info->ramdisk, 0);
|
|
xlu_cfg_replace_string (config, "device_tree", &b_info->device_tree, 0);
|
|
b_info->cmdline = parse_cmdline(config);
|
|
|
|
xlu_cfg_get_defbool(config, "driver_domain", &c_info->driver_domain, 0);
|
|
xlu_cfg_get_defbool(config, "acpi", &b_info->acpi, 0);
|
|
|
|
xlu_cfg_replace_string (config, "bootloader", &b_info->bootloader, 0);
|
|
#ifndef HAVE_PYGRUB
|
|
if (b_info->bootloader &&
|
|
(!strcmp(b_info->bootloader, "pygrub") ||
|
|
!strcmp(b_info->bootloader, "/usr/bin/pygrub"))) {
|
|
fprintf(stderr, "ERROR: this instance of Xen has been built without support of \"pygrub\".\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
#endif
|
|
xlu_cfg_get_defbool(config, "bootloader_restrict",
|
|
&b_info->bootloader_restrict, 0);
|
|
if (!libxl_defbool_is_default(bootloader_restrict))
|
|
libxl_defbool_setdefault(&b_info->bootloader_restrict,
|
|
libxl_defbool_val(bootloader_restrict));
|
|
xlu_cfg_replace_string(config, "bootloader_user",
|
|
&b_info->bootloader_user, 0);
|
|
|
|
switch (xlu_cfg_get_list_as_string_list(config, "bootloader_args",
|
|
&b_info->bootloader_args, 1)) {
|
|
case 0:
|
|
break; /* Success */
|
|
case ESRCH: break; /* Option not present */
|
|
case EINVAL:
|
|
if (!xlu_cfg_get_string(config, "bootloader_args", &buf, 0)) {
|
|
|
|
fprintf(stderr, "WARNING: Specifying \"bootloader_args\""
|
|
" as a string is deprecated. "
|
|
"Please use a list of arguments.\n");
|
|
split_string_into_string_list(buf, " \t\n",
|
|
&b_info->bootloader_args);
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr,"xl: Unable to parse bootloader_args.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
if (!xlu_cfg_get_long(config, "timer_mode", &l, 1)) {
|
|
const char *s = libxl_timer_mode_to_string(l);
|
|
|
|
if (b_info->type == LIBXL_DOMAIN_TYPE_PV) {
|
|
fprintf(stderr,
|
|
"ERROR: \"timer_mode\" option is not supported for PV guests.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
fprintf(stderr,
|
|
"WARNING: specifying \"timer_mode\" as an integer is deprecated. "
|
|
"Please use the named parameter variant. %s%s%s\n",
|
|
s ? "e.g. timer_mode=\"" : "",
|
|
s ? s : "",
|
|
s ? "\"" : "");
|
|
|
|
if (l < LIBXL_TIMER_MODE_DELAY_FOR_MISSED_TICKS ||
|
|
l > LIBXL_TIMER_MODE_ONE_MISSED_TICK_PENDING) {
|
|
fprintf(stderr, "ERROR: invalid value %ld for \"timer_mode\"\n",
|
|
l);
|
|
exit (1);
|
|
}
|
|
b_info->timer_mode = l;
|
|
} else if (!xlu_cfg_get_string(config, "timer_mode", &buf, 0)) {
|
|
if (b_info->type == LIBXL_DOMAIN_TYPE_PV) {
|
|
fprintf(stderr,
|
|
"ERROR: \"timer_mode\" option is not supported for PV guests.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
if (libxl_timer_mode_from_string(buf, &b_info->timer_mode)) {
|
|
fprintf(stderr,
|
|
"ERROR: invalid value \"%s\" for \"timer_mode\"\n", buf);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
xlu_cfg_get_defbool(config, "nestedhvm", &b_info->nested_hvm, 0);
|
|
|
|
switch(b_info->type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
kernel_basename = libxl_basename(b_info->kernel);
|
|
if (!strcmp(kernel_basename, "hvmloader")) {
|
|
fprintf(stderr, "WARNING: you seem to be using \"kernel\" "
|
|
"directive to override HVM guest firmware. Ignore "
|
|
"that. Use \"firmware_override\" instead if you "
|
|
"really want a non-default firmware\n");
|
|
b_info->kernel = NULL;
|
|
}
|
|
free(kernel_basename);
|
|
|
|
xlu_cfg_replace_string (config, "firmware_override",
|
|
&b_info->u.hvm.firmware, 0);
|
|
xlu_cfg_replace_string (config, "bios_path_override",
|
|
&b_info->u.hvm.system_firmware, 0);
|
|
if (!xlu_cfg_get_string(config, "bios", &buf, 0)) {
|
|
if (libxl_bios_type_from_string(buf, &b_info->u.hvm.bios)) {
|
|
fprintf(stderr, "ERROR: invalid value \"%s\" for \"bios\"\n",
|
|
buf);
|
|
exit (1);
|
|
}
|
|
} else if (b_info->u.hvm.system_firmware)
|
|
fprintf(stderr, "WARNING: "
|
|
"bios_path_override given without specific bios name\n");
|
|
|
|
xlu_cfg_get_defbool(config, "pae", &b_info->u.hvm.pae, 0);
|
|
xlu_cfg_get_defbool(config, "acpi_s3", &b_info->u.hvm.acpi_s3, 0);
|
|
xlu_cfg_get_defbool(config, "acpi_s4", &b_info->u.hvm.acpi_s4, 0);
|
|
xlu_cfg_get_defbool(config, "acpi_laptop_slate", &b_info->u.hvm.acpi_laptop_slate, 0);
|
|
xlu_cfg_get_defbool(config, "nx", &b_info->u.hvm.nx, 0);
|
|
xlu_cfg_get_defbool(config, "hpet", &b_info->u.hvm.hpet, 0);
|
|
xlu_cfg_get_defbool(config, "vpt_align", &b_info->u.hvm.vpt_align, 0);
|
|
xlu_cfg_get_defbool(config, "apic", &b_info->apic, 0);
|
|
xlu_cfg_get_defbool(config, "hvm_pirq", &b_info->u.hvm.pirq, 0);
|
|
|
|
switch (xlu_cfg_get_list(config, "viridian",
|
|
&viridian, &num_viridian, 1))
|
|
{
|
|
case 0: /* Success */
|
|
if (num_viridian) {
|
|
libxl_bitmap_alloc(ctx, &b_info->u.hvm.viridian_enable,
|
|
LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH);
|
|
libxl_bitmap_alloc(ctx, &b_info->u.hvm.viridian_disable,
|
|
LIBXL_BUILDINFO_HVM_VIRIDIAN_ENABLE_DISABLE_WIDTH);
|
|
}
|
|
for (i = 0; i < num_viridian; i++) {
|
|
libxl_viridian_enlightenment v;
|
|
|
|
buf = xlu_cfg_get_listitem(viridian, i);
|
|
if (strcmp(buf, "all") == 0)
|
|
libxl_bitmap_set_any(&b_info->u.hvm.viridian_enable);
|
|
else if (strcmp(buf, "defaults") == 0)
|
|
libxl_defbool_set(&b_info->u.hvm.viridian, true);
|
|
else {
|
|
libxl_bitmap *s = &b_info->u.hvm.viridian_enable;
|
|
libxl_bitmap *r = &b_info->u.hvm.viridian_disable;
|
|
|
|
if (*buf == '!') {
|
|
s = &b_info->u.hvm.viridian_disable;
|
|
r = &b_info->u.hvm.viridian_enable;
|
|
buf++;
|
|
}
|
|
|
|
e = libxl_viridian_enlightenment_from_string(buf, &v);
|
|
if (e) {
|
|
fprintf(stderr,
|
|
"xl: unknown viridian enlightenment '%s'\n",
|
|
buf);
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
libxl_bitmap_set(s, v);
|
|
libxl_bitmap_reset(r, v);
|
|
}
|
|
}
|
|
break;
|
|
case ESRCH: break; /* Option not present */
|
|
case EINVAL:
|
|
xlu_cfg_get_defbool(config, "viridian", &b_info->u.hvm.viridian, 1);
|
|
break;
|
|
default:
|
|
fprintf(stderr,"xl: Unable to parse viridian enlightenments.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
if (!xlu_cfg_get_long(config, "mmio_hole", &l, 0)) {
|
|
uint64_t mmio_hole_size;
|
|
|
|
b_info->u.hvm.mmio_hole_memkb = l * 1024;
|
|
mmio_hole_size = b_info->u.hvm.mmio_hole_memkb * 1024;
|
|
if (mmio_hole_size < HVM_BELOW_4G_MMIO_LENGTH ||
|
|
mmio_hole_size > HVM_BELOW_4G_MMIO_START) {
|
|
fprintf(stderr,
|
|
"ERROR: invalid value %ld for \"mmio_hole\"\n", l);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_defbool(config, "altp2mhvm", &b_info->u.hvm.altp2m, 0))
|
|
fprintf(stderr, "WARNING: Specifying \"altp2mhvm\" is deprecated. "
|
|
"Please use \"altp2m\" instead.\n");
|
|
|
|
xlu_cfg_replace_string(config, "smbios_firmware",
|
|
&b_info->u.hvm.smbios_firmware, 0);
|
|
xlu_cfg_replace_string(config, "acpi_firmware",
|
|
&b_info->u.hvm.acpi_firmware, 0);
|
|
|
|
switch (xlu_cfg_get_list(config, "smbios", &smbios, &num_smbios, 0))
|
|
{
|
|
case 0: /* Success */
|
|
{
|
|
unsigned int num_oem = 1;
|
|
|
|
b_info->u.hvm.num_smbios = num_smbios;
|
|
b_info->u.hvm.smbios = xcalloc(num_smbios, sizeof(libxl_smbios));
|
|
for (i = 0; i < num_smbios; i++) {
|
|
libxl_smbios_type type;
|
|
char *option;
|
|
char *value;
|
|
|
|
buf = xlu_cfg_get_listitem(smbios, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Unable to get element #%d in smbios list\n",
|
|
i);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
option = xstrdup(buf);
|
|
value = strchr(option, '=');
|
|
if (value == NULL) {
|
|
fprintf(stderr, "xl: failed to split \"%s\" at '='\n",
|
|
option);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
*value = '\0';
|
|
value++;
|
|
|
|
if (*value == '\0') {
|
|
fprintf(stderr,
|
|
"xl: empty value not allowed for smbios \"%s=\"\n",
|
|
option);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
e = libxl_smbios_type_from_string(option, &type);
|
|
if (e) {
|
|
fprintf(stderr, "xl: unknown smbios type '%s'\n", option);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (type == LIBXL_SMBIOS_TYPE_OEM) {
|
|
if (num_oem > 99) {
|
|
fprintf(stderr,
|
|
"xl: smbios limited to 99 oem strings\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
num_oem++;
|
|
}
|
|
|
|
b_info->u.hvm.smbios[i].key = type;
|
|
b_info->u.hvm.smbios[i].value = xstrdup(value);
|
|
|
|
free(option);
|
|
}
|
|
break;
|
|
}
|
|
case ESRCH: /* Option not present */
|
|
break;
|
|
default:
|
|
fprintf(stderr,"xl: Unable to parse smbios options.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!xlu_cfg_get_string(config, "ms_vm_genid", &buf, 0)) {
|
|
if (!strcmp(buf, "generate")) {
|
|
e = libxl_ms_vm_genid_generate(ctx, &b_info->u.hvm.ms_vm_genid);
|
|
if (e) {
|
|
fprintf(stderr, "ERROR: failed to generate a VM Generation ID\n");
|
|
exit(1);
|
|
}
|
|
} else if (!strcmp(buf, "none")) {
|
|
;
|
|
} else {
|
|
fprintf(stderr, "ERROR: \"ms_vm_genid\" option must be \"generate\" or \"none\"\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_long (config, "rdm_mem_boundary", &l, 0))
|
|
b_info->u.hvm.rdm_mem_boundary_memkb = l * 1024;
|
|
|
|
switch (xlu_cfg_get_list(config, "mca_caps",
|
|
&mca_caps, &num_mca_caps, 1))
|
|
{
|
|
case 0: /* Success */
|
|
for (i = 0; i < num_mca_caps; i++) {
|
|
buf = xlu_cfg_get_listitem(mca_caps, i);
|
|
if (!strcmp(buf, "lmce"))
|
|
b_info->u.hvm.mca_caps |= XEN_HVM_MCA_CAP_LMCE;
|
|
else {
|
|
fprintf(stderr, "ERROR: unrecognized MCA capability '%s'.\n",
|
|
buf);
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ESRCH: /* Option not present */
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "ERROR: unable to parse mca_caps.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
/*
|
|
* The firmware config option can be used as a simplification
|
|
* instead of setting bios or firmware_override. It has the
|
|
* following meanings for HVM guests:
|
|
*
|
|
* - ovmf | seabios | rombios: maps directly into the "bios"
|
|
* option.
|
|
* - uefi | bios: maps into one of the above options and is set
|
|
* in the bios field.
|
|
* - Anything else is treated as a path that is copied into
|
|
* firmware.
|
|
*/
|
|
if (!xlu_cfg_get_string (config, "firmware", &buf, 0) &&
|
|
libxl_bios_type_from_string(buf, &b_info->u.hvm.bios)) {
|
|
if (!strncmp(buf, "uefi", strlen(buf)))
|
|
b_info->u.hvm.bios = LIBXL_BIOS_TYPE_OVMF;
|
|
else if (strncmp(buf, "bios", strlen(buf)))
|
|
/* Assume it's a path to a custom firmware. */
|
|
xlu_cfg_replace_string(config, "firmware",
|
|
&b_info->u.hvm.firmware, 0);
|
|
/*
|
|
* BIOS is the default, and will be chosen by libxl based on
|
|
* the device model specified.
|
|
*/
|
|
}
|
|
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PVH:
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
{
|
|
/*
|
|
* The firmware config option can be used as a simplification
|
|
* instead of directly setting kernel. It will be translated to
|
|
* XENFIRMWAREDIR/<string>.bin
|
|
*/
|
|
if (!xlu_cfg_get_string (config, "firmware", &buf, 0)) {
|
|
if (b_info->kernel) {
|
|
fprintf(stderr,
|
|
"ERROR: both kernel and firmware specified\n");
|
|
exit(1);
|
|
}
|
|
if (strncmp(buf, "pvgrub32", strlen(buf)) &&
|
|
strncmp(buf, "pvgrub64", strlen(buf))) {
|
|
fprintf(stderr,
|
|
"ERROR: only pvgrub{32|64} supported as firmware options\n");
|
|
exit(1);
|
|
}
|
|
|
|
xasprintf(&b_info->kernel, XENFIRMWAREDIR "/%s.bin", buf);
|
|
}
|
|
if (!b_info->bootloader && !b_info->kernel) {
|
|
fprintf(stderr, "Neither kernel nor bootloader specified\n");
|
|
exit(1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
if (!xlu_cfg_get_long(config, "altp2m", &l, 1)) {
|
|
if (l < LIBXL_ALTP2M_MODE_DISABLED ||
|
|
l > LIBXL_ALTP2M_MODE_LIMITED) {
|
|
fprintf(stderr, "ERROR: invalid value %ld for \"altp2m\"\n", l);
|
|
exit (1);
|
|
}
|
|
|
|
b_info->altp2m = l;
|
|
} else if (!xlu_cfg_get_string(config, "altp2m", &buf, 0)) {
|
|
if (libxl_altp2m_mode_from_string(buf, &b_info->altp2m)) {
|
|
fprintf(stderr, "ERROR: invalid value \"%s\" for \"altp2m\"\n",
|
|
buf);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_long(config, "altp2m_count", &l, 1)) {
|
|
if (l != (uint16_t)l) {
|
|
fprintf(stderr, "ERROR: invalid value %ld for \"altp2m_count\"\n", l);
|
|
exit (1);
|
|
}
|
|
|
|
b_info->altp2m_count = l;
|
|
}
|
|
|
|
if (!xlu_cfg_get_long(config, "vmtrace_buf_kb", &l, 1) && l) {
|
|
b_info->vmtrace_buf_kb = l;
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "ioports", &ioports, &num_ioports, 0)) {
|
|
b_info->num_ioports = num_ioports;
|
|
b_info->ioports = calloc(num_ioports, sizeof(*b_info->ioports));
|
|
if (b_info->ioports == NULL) {
|
|
fprintf(stderr, "unable to allocate memory for ioports\n");
|
|
exit(-1);
|
|
}
|
|
|
|
for (i = 0; i < num_ioports; i++) {
|
|
const char *buf2;
|
|
char *ep;
|
|
uint32_t start, end;
|
|
unsigned long ul;
|
|
|
|
buf = xlu_cfg_get_listitem (ioports, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Unable to get element #%d in ioport list\n", i);
|
|
exit(1);
|
|
}
|
|
ul = strtoul(buf, &ep, 16);
|
|
if (ep == buf) {
|
|
fprintf(stderr, "xl: Invalid argument parsing ioport: %s\n",
|
|
buf);
|
|
exit(1);
|
|
}
|
|
if (ul >= UINT32_MAX) {
|
|
fprintf(stderr, "xl: ioport %lx too big\n", ul);
|
|
exit(1);
|
|
}
|
|
start = end = ul;
|
|
|
|
if (*ep == '-') {
|
|
buf2 = ep + 1;
|
|
ul = strtoul(buf2, &ep, 16);
|
|
if (ep == buf2 || *ep != '\0' || start > end) {
|
|
fprintf(stderr,
|
|
"xl: Invalid argument parsing ioport: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
if (ul >= UINT32_MAX) {
|
|
fprintf(stderr, "xl: ioport %lx too big\n", ul);
|
|
exit(1);
|
|
}
|
|
end = ul;
|
|
} else if ( *ep != '\0' )
|
|
fprintf(stderr,
|
|
"xl: Invalid argument parsing ioport: %s\n", buf);
|
|
b_info->ioports[i].first = start;
|
|
b_info->ioports[i].number = end - start + 1;
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "irqs", &irqs, &num_irqs, 0)) {
|
|
b_info->num_irqs = num_irqs;
|
|
b_info->irqs = calloc(num_irqs, sizeof(*b_info->irqs));
|
|
if (b_info->irqs == NULL) {
|
|
fprintf(stderr, "unable to allocate memory for ioports\n");
|
|
exit(-1);
|
|
}
|
|
for (i = 0; i < num_irqs; i++) {
|
|
char *ep;
|
|
unsigned long ul;
|
|
buf = xlu_cfg_get_listitem (irqs, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Unable to get element %d in irq list\n", i);
|
|
exit(1);
|
|
}
|
|
ul = strtoul(buf, &ep, 10);
|
|
if (ep == buf || *ep != '\0') {
|
|
fprintf(stderr,
|
|
"xl: Invalid argument parsing irq: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
if (ul >= UINT32_MAX) {
|
|
fprintf(stderr, "xl: irq %lx too big\n", ul);
|
|
exit(1);
|
|
}
|
|
b_info->irqs[i] = ul;
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "iomem", &iomem, &num_iomem, 0)) {
|
|
int ret;
|
|
b_info->num_iomem = num_iomem;
|
|
b_info->iomem = calloc(num_iomem, sizeof(*b_info->iomem));
|
|
if (b_info->iomem == NULL) {
|
|
fprintf(stderr, "unable to allocate memory for iomem\n");
|
|
exit(-1);
|
|
}
|
|
for (i = 0; i < num_iomem; i++) {
|
|
int used;
|
|
|
|
buf = xlu_cfg_get_listitem (iomem, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Unable to get element %d in iomem list\n", i);
|
|
exit(1);
|
|
}
|
|
libxl_iomem_range_init(&b_info->iomem[i]);
|
|
ret = sscanf(buf, "%" SCNx64",%" SCNx64"%n@%" SCNx64"%n",
|
|
&b_info->iomem[i].start,
|
|
&b_info->iomem[i].number, &used,
|
|
&b_info->iomem[i].gfn, &used);
|
|
if (ret < 2 || buf[used] != '\0') {
|
|
fprintf(stderr,
|
|
"xl: Invalid argument parsing iomem: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (!xlu_cfg_get_list (config, "disk", &vbds, 0, 0)) {
|
|
d_config->num_disks = 0;
|
|
d_config->disks = NULL;
|
|
while ((buf = xlu_cfg_get_listitem (vbds, d_config->num_disks)) != NULL) {
|
|
libxl_device_disk *disk;
|
|
char *buf2 = strdup(buf);
|
|
|
|
disk = ARRAY_EXTEND_INIT_NODEVID(d_config->disks,
|
|
d_config->num_disks,
|
|
libxl_device_disk_init);
|
|
parse_disk_config(&config, buf2, disk);
|
|
|
|
free(buf2);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "p9", &p9devs, 0, 0)) {
|
|
libxl_device_p9 *p9;
|
|
|
|
d_config->num_p9s = 0;
|
|
d_config->p9s = NULL;
|
|
while ((buf = xlu_cfg_get_listitem (p9devs, d_config->num_p9s)) != NULL) {
|
|
libxl_string_list pairs;
|
|
int len;
|
|
|
|
p9 = ARRAY_EXTEND_INIT(d_config->p9s,
|
|
d_config->num_p9s,
|
|
libxl_device_p9_init);
|
|
libxl_device_p9_init(p9);
|
|
|
|
split_string_into_string_list(buf, ",", &pairs);
|
|
len = libxl_string_list_length(&pairs);
|
|
for (i = 0; i < len; i++) {
|
|
char *key, *value;
|
|
int rc;
|
|
|
|
rc = split_string_into_pair(pairs[i], '=', &key, &value,
|
|
isspace);
|
|
if (rc != 0) {
|
|
fprintf(stderr, "failed to parse 9pfs configuration: %s",
|
|
pairs[i]);
|
|
exit(1);
|
|
}
|
|
|
|
if (!strcmp(key, "security_model")) {
|
|
replace_string(&p9->security_model, value);
|
|
} else if (!strcmp(key, "path")) {
|
|
replace_string(&p9->path, value);
|
|
} else if (!strcmp(key, "tag")) {
|
|
replace_string(&p9->tag, value);
|
|
} else if (!strcmp(key, "backend")) {
|
|
replace_string(&p9->backend_domname, value);
|
|
} else if (!strcmp(key, "type")) {
|
|
if (libxl_p9_type_from_string(value, &p9->type)) {
|
|
fprintf(stderr, "failed to parse 9pfs type: %s\n",
|
|
value);
|
|
exit(1);
|
|
}
|
|
} else if (!strcmp(key, "max-files")) {
|
|
p9->max_files = parse_ulong(value);
|
|
} else if (!strcmp(key, "max-open-files")) {
|
|
p9->max_open_files = parse_ulong(value);
|
|
} else if (!strcmp(key, "max-space")) {
|
|
p9->max_space = parse_ulong(value);
|
|
} else if (!strcmp(key, "auto-delete")) {
|
|
p9->auto_delete = strtoul(value, NULL, 0);
|
|
} else {
|
|
fprintf(stderr, "Unknown 9pfs parameter '%s'\n", key);
|
|
exit(1);
|
|
}
|
|
free(key);
|
|
free(value);
|
|
}
|
|
|
|
libxl_string_list_dispose(&pairs);
|
|
|
|
if (p9->type == LIBXL_P9_TYPE_XEN_9PFSD && !p9->path) {
|
|
char *path;
|
|
|
|
xasprintf(&path, XEN_LOG_DIR "/guests/%s", c_info->name);
|
|
replace_string(&p9->path, path);
|
|
free(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "vtpm", &vtpms, 0, 0)) {
|
|
d_config->num_vtpms = 0;
|
|
d_config->vtpms = NULL;
|
|
while ((buf = xlu_cfg_get_listitem (vtpms, d_config->num_vtpms)) != NULL) {
|
|
libxl_device_vtpm *vtpm;
|
|
char * buf2 = strdup(buf);
|
|
char *p, *p2;
|
|
bool got_backend = false;
|
|
|
|
vtpm = ARRAY_EXTEND_INIT(d_config->vtpms,
|
|
d_config->num_vtpms,
|
|
libxl_device_vtpm_init);
|
|
|
|
p = strtok(buf2, ",");
|
|
if(p) {
|
|
do {
|
|
while(*p == ' ')
|
|
++p;
|
|
if ((p2 = strchr(p, '=')) == NULL)
|
|
break;
|
|
*p2 = '\0';
|
|
if (!strcmp(p, "backend")) {
|
|
vtpm->backend_domname = strdup(p2 + 1);
|
|
got_backend = true;
|
|
} else if(!strcmp(p, "uuid")) {
|
|
if( libxl_uuid_from_string(&vtpm->uuid, p2 + 1) ) {
|
|
fprintf(stderr,
|
|
"Failed to parse vtpm UUID: %s\n", p2 + 1);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Unknown string `%s' in vtpm spec\n", p);
|
|
exit(1);
|
|
}
|
|
} while ((p = strtok(NULL, ",")) != NULL);
|
|
}
|
|
if(!got_backend) {
|
|
fprintf(stderr, "vtpm spec missing required backend field!\n");
|
|
exit(1);
|
|
}
|
|
free(buf2);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "vdispl", &vdispls, 0, 0)) {
|
|
d_config->num_vdispls = 0;
|
|
d_config->vdispls = NULL;
|
|
while ((buf = xlu_cfg_get_listitem(vdispls, d_config->num_vdispls)) != NULL) {
|
|
libxl_device_vdispl *vdispl;
|
|
char * buf2 = strdup(buf);
|
|
char *p;
|
|
vdispl = ARRAY_EXTEND_INIT(d_config->vdispls,
|
|
d_config->num_vdispls,
|
|
libxl_device_vdispl_init);
|
|
p = strtok (buf2, ",");
|
|
while (p != NULL)
|
|
{
|
|
while (*p == ' ') p++;
|
|
if (parse_vdispl_config(vdispl, p)) {
|
|
free(buf2);
|
|
exit(1);
|
|
}
|
|
p = strtok (NULL, ",");
|
|
}
|
|
free(buf2);
|
|
if (vdispl->num_connectors == 0) {
|
|
fprintf(stderr, "At least one connector should be specified.\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "pvcalls", &pvcallsifs_devs, 0, 0)) {
|
|
d_config->num_pvcallsifs = 0;
|
|
d_config->pvcallsifs = NULL;
|
|
while ((buf = xlu_cfg_get_listitem (pvcallsifs_devs, d_config->num_pvcallsifs)) != NULL) {
|
|
libxl_device_pvcallsif *pvcallsif;
|
|
char *backend = NULL;
|
|
char *p, *p2, *buf2;
|
|
pvcallsif = ARRAY_EXTEND_INIT(d_config->pvcallsifs,
|
|
d_config->num_pvcallsifs,
|
|
libxl_device_pvcallsif_init);
|
|
|
|
buf2 = strdup(buf);
|
|
p = strtok(buf2, ",");
|
|
if (p) {
|
|
do {
|
|
while (*p == ' ')
|
|
++p;
|
|
if ((p2 = strchr(p, '=')) == NULL)
|
|
break;
|
|
*p2 = '\0';
|
|
if(!strcmp(p, "backend")) {
|
|
backend = strdup(p2 + 1);
|
|
} else {
|
|
fprintf(stderr, "Unknown string `%s' in pvcalls spec\n", p);
|
|
exit(1);
|
|
}
|
|
} while ((p = strtok(NULL, ",")) != NULL);
|
|
}
|
|
free(buf2);
|
|
|
|
if (backend)
|
|
replace_string(&pvcallsif->backend_domname, backend);
|
|
}
|
|
}
|
|
|
|
parse_vsnd_config(config, d_config);
|
|
|
|
if (!xlu_cfg_get_list (config, "channel", &channels, 0, 0)) {
|
|
d_config->num_channels = 0;
|
|
d_config->channels = NULL;
|
|
while ((buf = xlu_cfg_get_listitem (channels,
|
|
d_config->num_channels)) != NULL) {
|
|
libxl_device_channel *chn;
|
|
libxl_string_list pairs;
|
|
char *path = NULL;
|
|
int len;
|
|
|
|
chn = ARRAY_EXTEND_INIT_NODEVID(d_config->channels,
|
|
d_config->num_channels,
|
|
libxl_device_channel_init);
|
|
|
|
split_string_into_string_list(buf, ",", &pairs);
|
|
len = libxl_string_list_length(&pairs);
|
|
for (i = 0; i < len; i++) {
|
|
char *key, *value;
|
|
int rc;
|
|
rc = split_string_into_pair(pairs[i], '=', &key, &value,
|
|
isspace);
|
|
if (rc != 0) {
|
|
fprintf(stderr, "failed to parse channel configuration: %s",
|
|
pairs[i]);
|
|
exit(1);
|
|
}
|
|
|
|
if (!strcmp(key, "backend")) {
|
|
replace_string(&chn->backend_domname, value);
|
|
} else if (!strcmp(key, "name")) {
|
|
replace_string(&chn->name, value);
|
|
} else if (!strcmp(key, "path")) {
|
|
replace_string(&path, value);
|
|
} else if (!strcmp(key, "connection")) {
|
|
if (!strcmp(value, "pty")) {
|
|
chn->connection = LIBXL_CHANNEL_CONNECTION_PTY;
|
|
} else if (!strcmp(value, "socket")) {
|
|
chn->connection = LIBXL_CHANNEL_CONNECTION_SOCKET;
|
|
} else {
|
|
fprintf(stderr, "unknown channel connection '%s'\n",
|
|
value);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "unknown channel parameter '%s',"
|
|
" ignoring\n", key);
|
|
}
|
|
free(key);
|
|
free(value);
|
|
}
|
|
switch (chn->connection) {
|
|
case LIBXL_CHANNEL_CONNECTION_UNKNOWN:
|
|
fprintf(stderr, "channel has unknown 'connection'\n");
|
|
exit(1);
|
|
case LIBXL_CHANNEL_CONNECTION_SOCKET:
|
|
if (!path) {
|
|
fprintf(stderr, "channel connection 'socket' requires path=..\n");
|
|
exit(1);
|
|
}
|
|
chn->u.socket.path = xstrdup(path);
|
|
break;
|
|
case LIBXL_CHANNEL_CONNECTION_PTY:
|
|
/* Nothing to do since PTY has no arguments */
|
|
break;
|
|
default:
|
|
fprintf(stderr, "unknown channel connection: %d",
|
|
chn->connection);
|
|
exit(1);
|
|
}
|
|
libxl_string_list_dispose(&pairs);
|
|
free(path);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list (config, "vif", &nics, 0, 0)) {
|
|
d_config->num_nics = 0;
|
|
d_config->nics = NULL;
|
|
while ((buf = xlu_cfg_get_listitem (nics, d_config->num_nics)) != NULL) {
|
|
libxl_device_nic *nic;
|
|
char *buf2 = strdup(buf);
|
|
char *p;
|
|
|
|
nic = ARRAY_EXTEND_INIT(d_config->nics,
|
|
d_config->num_nics,
|
|
libxl_device_nic_init);
|
|
set_default_nic_values(nic);
|
|
|
|
p = strtok(buf2, ",");
|
|
if (!p)
|
|
goto skip_nic;
|
|
do {
|
|
while (*p == ' ')
|
|
p++;
|
|
parse_nic_config(nic, &config, p);
|
|
} while ((p = strtok(NULL, ",")) != NULL);
|
|
skip_nic:
|
|
free(buf2);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "vif2", NULL, 0, 0)) {
|
|
fprintf(stderr, "WARNING: vif2: netchannel2 is deprecated and not supported by xl\n");
|
|
}
|
|
|
|
d_config->num_vfbs = 0;
|
|
d_config->num_vkbs = 0;
|
|
d_config->vfbs = NULL;
|
|
d_config->vkbs = NULL;
|
|
|
|
if (!xlu_cfg_get_list (config, "vfb", &cvfbs, 0, 0)) {
|
|
while ((buf = xlu_cfg_get_listitem (cvfbs, d_config->num_vfbs)) != NULL) {
|
|
libxl_device_vfb *vfb;
|
|
libxl_device_vkb *vkb;
|
|
|
|
char *buf2 = strdup(buf);
|
|
char *p, *p2;
|
|
|
|
vfb = ARRAY_EXTEND_INIT(d_config->vfbs, d_config->num_vfbs,
|
|
libxl_device_vfb_init);
|
|
|
|
vkb = ARRAY_EXTEND_INIT(d_config->vkbs, d_config->num_vkbs,
|
|
libxl_device_vkb_init);
|
|
|
|
p = strtok(buf2, ",");
|
|
if (!p)
|
|
goto skip_vfb;
|
|
do {
|
|
while (*p == ' ')
|
|
p++;
|
|
if ((p2 = strchr(p, '=')) == NULL)
|
|
break;
|
|
*p2 = '\0';
|
|
if (!strcmp(p, "vnc")) {
|
|
libxl_defbool_set(&vfb->vnc.enable, atoi(p2 + 1));
|
|
} else if (!strcmp(p, "vnclisten")) {
|
|
free(vfb->vnc.listen);
|
|
vfb->vnc.listen = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "vncpasswd")) {
|
|
free(vfb->vnc.passwd);
|
|
vfb->vnc.passwd = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "vncdisplay")) {
|
|
vfb->vnc.display = atoi(p2 + 1);
|
|
} else if (!strcmp(p, "vncunused")) {
|
|
libxl_defbool_set(&vfb->vnc.findunused, atoi(p2 + 1));
|
|
} else if (!strcmp(p, "keymap")) {
|
|
free(vfb->keymap);
|
|
vfb->keymap = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "sdl")) {
|
|
libxl_defbool_set(&vfb->sdl.enable, atoi(p2 + 1));
|
|
} else if (!strcmp(p, "opengl")) {
|
|
libxl_defbool_set(&vfb->sdl.opengl, atoi(p2 + 1));
|
|
} else if (!strcmp(p, "display")) {
|
|
free(vfb->sdl.display);
|
|
vfb->sdl.display = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "xauthority")) {
|
|
free(vfb->sdl.xauthority);
|
|
vfb->sdl.xauthority = strdup(p2 + 1);
|
|
}
|
|
} while ((p = strtok(NULL, ",")) != NULL);
|
|
|
|
skip_vfb:
|
|
free(buf2);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "usbctrl", &usbctrls, 0, 0)) {
|
|
d_config->num_usbctrls = 0;
|
|
d_config->usbctrls = NULL;
|
|
while ((buf = xlu_cfg_get_listitem(usbctrls, d_config->num_usbctrls))
|
|
!= NULL) {
|
|
libxl_device_usbctrl *usbctrl;
|
|
char *buf2 = strdup(buf);
|
|
char *p;
|
|
|
|
usbctrl = ARRAY_EXTEND_INIT(d_config->usbctrls,
|
|
d_config->num_usbctrls,
|
|
libxl_device_usbctrl_init);
|
|
p = strtok(buf2, ",");
|
|
if (!p)
|
|
goto skip_usbctrl;
|
|
do {
|
|
while (*p == ' ')
|
|
p++;
|
|
if (parse_usbctrl_config(usbctrl, p))
|
|
exit(1);
|
|
} while ((p = strtok(NULL, ",")) != NULL);
|
|
skip_usbctrl:
|
|
free(buf2);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "usbdev", &usbdevs, 0, 0)) {
|
|
d_config->num_usbdevs = 0;
|
|
d_config->usbdevs = NULL;
|
|
while ((buf = xlu_cfg_get_listitem(usbdevs, d_config->num_usbdevs))
|
|
!= NULL) {
|
|
libxl_device_usbdev *usbdev;
|
|
char *buf2 = strdup(buf);
|
|
char *p;
|
|
|
|
usbdev = ARRAY_EXTEND_INIT_NODEVID(d_config->usbdevs,
|
|
d_config->num_usbdevs,
|
|
libxl_device_usbdev_init);
|
|
p = strtok(buf2, ",");
|
|
if (!p)
|
|
goto skip_usbdev;
|
|
do {
|
|
while (*p == ' ')
|
|
p++;
|
|
if (parse_usbdev_config(usbdev, p))
|
|
exit(1);
|
|
} while ((p = strtok(NULL, ",")) != NULL);
|
|
skip_usbdev:
|
|
free(buf2);
|
|
}
|
|
}
|
|
|
|
switch (xlu_cfg_get_list(config, "cpuid", &cpuids, 0, 1)) {
|
|
case 0:
|
|
{
|
|
const char *errstr;
|
|
|
|
for (i = 0; (buf = xlu_cfg_get_listitem(cpuids, i)) != NULL; i++) {
|
|
e = libxl_cpuid_parse_config_xend(&b_info->cpuid, buf);
|
|
switch (e) {
|
|
case 0: continue;
|
|
case 1:
|
|
errstr = "illegal leaf number";
|
|
break;
|
|
case 2:
|
|
errstr = "illegal subleaf number";
|
|
break;
|
|
case 3:
|
|
errstr = "missing colon";
|
|
break;
|
|
case 4:
|
|
errstr = "invalid register name (must be e[abcd]x)";
|
|
break;
|
|
case 5:
|
|
errstr = "policy string must be exactly 32 characters long";
|
|
break;
|
|
default:
|
|
errstr = "unknown error";
|
|
break;
|
|
}
|
|
fprintf(stderr, "while parsing CPUID line: \"%s\":\n", buf);
|
|
fprintf(stderr, " error #%i: %s\n", e, errstr);
|
|
}
|
|
}
|
|
break;
|
|
case EINVAL: /* config option is not a list, parse as a string */
|
|
if (!xlu_cfg_get_string(config, "cpuid", &buf, 0)) {
|
|
char *buf2, *p, *strtok_ptr = NULL;
|
|
const char *errstr;
|
|
|
|
buf2 = strdup(buf);
|
|
p = strtok_r(buf2, ",", &strtok_ptr);
|
|
if (p == NULL) {
|
|
free(buf2);
|
|
break;
|
|
}
|
|
if (strcmp(p, "host")) {
|
|
fprintf(stderr, "while parsing CPUID string: \"%s\":\n", buf);
|
|
fprintf(stderr, " error: first word must be \"host\"\n");
|
|
free(buf2);
|
|
break;
|
|
}
|
|
for (p = strtok_r(NULL, ",", &strtok_ptr); p != NULL;
|
|
p = strtok_r(NULL, ",", &strtok_ptr)) {
|
|
e = libxl_cpuid_parse_config(&b_info->cpuid, p);
|
|
switch (e) {
|
|
case 0: continue;
|
|
case 1:
|
|
errstr = "missing \"=\" in key=value";
|
|
break;
|
|
case 2:
|
|
errstr = "unknown CPUID flag name";
|
|
break;
|
|
case 3:
|
|
errstr = "illegal CPUID value (must be: [0|1|x|k|s])";
|
|
break;
|
|
case ERROR_NOMEM:
|
|
errstr = "out of memory";
|
|
break;
|
|
default:
|
|
errstr = "unknown error";
|
|
break;
|
|
}
|
|
fprintf(stderr, "while parsing CPUID flag: \"%s\":\n", p);
|
|
fprintf(stderr, " error #%i: %s\n", e, errstr);
|
|
}
|
|
free(buf2);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* parse device model arguments, this works for pv, hvm and stubdom */
|
|
if (!xlu_cfg_get_string (config, "device_model", &buf, 0)) {
|
|
fprintf(stderr,
|
|
"WARNING: ignoring device_model directive.\n"
|
|
"WARNING: Use \"device_model_override\" instead if you"
|
|
" really want a non-default device_model\n");
|
|
if (strstr(buf, "stubdom-dm")) {
|
|
if (c_info->type == LIBXL_DOMAIN_TYPE_HVM)
|
|
fprintf(stderr, "WARNING: Or use"
|
|
" \"device_model_stubdomain_override\" if you "
|
|
" want to enable stubdomains\n");
|
|
else
|
|
fprintf(stderr, "WARNING: ignoring"
|
|
" \"device_model_stubdomain_override\" directive"
|
|
" for pv guest\n");
|
|
}
|
|
}
|
|
|
|
|
|
xlu_cfg_replace_string (config, "device_model_override",
|
|
&b_info->device_model, 0);
|
|
if (!xlu_cfg_get_string (config, "device_model_version", &buf, 0)) {
|
|
if (!strcmp(buf, "qemu-xen")) {
|
|
b_info->device_model_version
|
|
= LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
|
|
} else {
|
|
fprintf(stderr,
|
|
"Unknown device_model_version \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
} else if (b_info->device_model)
|
|
fprintf(stderr, "WARNING: device model override given without specific DM version\n");
|
|
xlu_cfg_get_defbool (config, "device_model_stubdomain_override",
|
|
&b_info->device_model_stubdomain, 0);
|
|
|
|
if (!xlu_cfg_get_string (config, "device_model_stubdomain_seclabel",
|
|
&buf, 0))
|
|
xlu_cfg_replace_string(config, "device_model_stubdomain_seclabel",
|
|
&b_info->device_model_ssid_label, 0);
|
|
|
|
xlu_cfg_replace_string(config, "device_model_user",
|
|
&b_info->device_model_user, 0);
|
|
|
|
xlu_cfg_replace_string (config, "stubdomain_kernel",
|
|
&b_info->stubdomain_kernel, 0);
|
|
xlu_cfg_replace_string (config, "stubdomain_cmdline",
|
|
&b_info->stubdomain_cmdline, 0);
|
|
xlu_cfg_replace_string (config, "stubdomain_ramdisk",
|
|
&b_info->stubdomain_ramdisk, 0);
|
|
if (!xlu_cfg_get_long (config, "stubdomain_memory", &l, 0))
|
|
b_info->stubdomain_memkb = l * 1024;
|
|
|
|
#define parse_extra_args(type) \
|
|
e = xlu_cfg_get_list_as_string_list(config, "device_model_args"#type, \
|
|
&b_info->extra##type, 0); \
|
|
if (e && e != ESRCH) { \
|
|
fprintf(stderr,"xl: Unable to parse device_model_args"#type".\n");\
|
|
exit(-ERROR_FAIL); \
|
|
}
|
|
|
|
/* parse extra args for qemu, common to both pv, hvm */
|
|
parse_extra_args();
|
|
|
|
/* parse extra args dedicated to pv */
|
|
parse_extra_args(_pv);
|
|
|
|
/* parse extra args dedicated to hvm */
|
|
parse_extra_args(_hvm);
|
|
|
|
#undef parse_extra_args
|
|
|
|
/* If we've already got vfb=[] for PV guest then ignore top level
|
|
* VNC config. */
|
|
if (c_info->type == LIBXL_DOMAIN_TYPE_PV && !d_config->num_vfbs) {
|
|
long vnc_enabled = 0;
|
|
|
|
if (!xlu_cfg_get_long (config, "vnc", &l, 0))
|
|
vnc_enabled = l;
|
|
|
|
if (vnc_enabled) {
|
|
libxl_device_vfb *vfb;
|
|
libxl_device_vkb *vkb;
|
|
|
|
vfb = ARRAY_EXTEND_INIT(d_config->vfbs, d_config->num_vfbs,
|
|
libxl_device_vfb_init);
|
|
|
|
vkb = ARRAY_EXTEND_INIT(d_config->vkbs, d_config->num_vkbs,
|
|
libxl_device_vkb_init);
|
|
|
|
parse_top_level_vnc_options(config, &vfb->vnc);
|
|
parse_top_level_sdl_options(config, &vfb->sdl);
|
|
xlu_cfg_replace_string (config, "keymap", &vfb->keymap, 0);
|
|
}
|
|
} else {
|
|
parse_top_level_vnc_options(config, &b_info->u.hvm.vnc);
|
|
parse_top_level_sdl_options(config, &b_info->u.hvm.sdl);
|
|
}
|
|
|
|
xlu_cfg_get_defbool(config, "dm_restrict", &b_info->dm_restrict, 0);
|
|
|
|
if (c_info->type == LIBXL_DOMAIN_TYPE_HVM) {
|
|
if (!xlu_cfg_get_string (config, "vga", &buf, 0)) {
|
|
if (!strcmp(buf, "stdvga")) {
|
|
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_STD;
|
|
} else if (!strcmp(buf, "cirrus")) {
|
|
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
|
|
} else if (!strcmp(buf, "none")) {
|
|
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_NONE;
|
|
} else if (!strcmp(buf, "qxl")) {
|
|
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_QXL;
|
|
} else {
|
|
fprintf(stderr, "Unknown vga \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
} else if (!xlu_cfg_get_long(config, "stdvga", &l, 0))
|
|
b_info->u.hvm.vga.kind = l ? LIBXL_VGA_INTERFACE_TYPE_STD :
|
|
LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
|
|
|
|
if (!xlu_cfg_get_string(config, "hdtype", &buf, 0) &&
|
|
libxl_hdtype_from_string(buf, &b_info->u.hvm.hdtype)) {
|
|
fprintf(stderr, "ERROR: invalid value \"%s\" for \"hdtype\"\n",
|
|
buf);
|
|
exit (1);
|
|
}
|
|
|
|
xlu_cfg_replace_string (config, "keymap", &b_info->u.hvm.keymap, 0);
|
|
xlu_cfg_get_defbool (config, "spice", &b_info->u.hvm.spice.enable, 0);
|
|
if (!xlu_cfg_get_long (config, "spiceport", &l, 0))
|
|
b_info->u.hvm.spice.port = l;
|
|
if (!xlu_cfg_get_long (config, "spicetls_port", &l, 0))
|
|
b_info->u.hvm.spice.tls_port = l;
|
|
xlu_cfg_replace_string (config, "spicehost",
|
|
&b_info->u.hvm.spice.host, 0);
|
|
xlu_cfg_get_defbool(config, "spicedisable_ticketing",
|
|
&b_info->u.hvm.spice.disable_ticketing, 0);
|
|
xlu_cfg_replace_string (config, "spicepasswd",
|
|
&b_info->u.hvm.spice.passwd, 0);
|
|
xlu_cfg_get_defbool(config, "spiceagent_mouse",
|
|
&b_info->u.hvm.spice.agent_mouse, 0);
|
|
xlu_cfg_get_defbool(config, "spicevdagent",
|
|
&b_info->u.hvm.spice.vdagent, 0);
|
|
xlu_cfg_get_defbool(config, "spice_clipboard_sharing",
|
|
&b_info->u.hvm.spice.clipboard_sharing, 0);
|
|
if (!xlu_cfg_get_long (config, "spiceusbredirection", &l, 0))
|
|
b_info->u.hvm.spice.usbredirection = l;
|
|
xlu_cfg_replace_string (config, "spice_image_compression",
|
|
&b_info->u.hvm.spice.image_compression, 0);
|
|
xlu_cfg_replace_string (config, "spice_streaming_video",
|
|
&b_info->u.hvm.spice.streaming_video, 0);
|
|
xlu_cfg_get_defbool(config, "nographic", &b_info->u.hvm.nographic, 0);
|
|
if (!xlu_cfg_get_long(config, "gfx_passthru", &l, 1)) {
|
|
libxl_defbool_set(&b_info->u.hvm.gfx_passthru, l);
|
|
} else if (!xlu_cfg_get_string(config, "gfx_passthru", &buf, 0)) {
|
|
if (libxl_gfx_passthru_kind_from_string(buf,
|
|
&b_info->u.hvm.gfx_passthru_kind)) {
|
|
fprintf(stderr,
|
|
"ERROR: invalid value \"%s\" for \"gfx_passthru\"\n",
|
|
buf);
|
|
exit (1);
|
|
}
|
|
libxl_defbool_set(&b_info->u.hvm.gfx_passthru, true);
|
|
}
|
|
switch (xlu_cfg_get_list_as_string_list(config, "serial",
|
|
&b_info->u.hvm.serial_list,
|
|
1))
|
|
{
|
|
|
|
case 0: break; /* Success */
|
|
case ESRCH: break; /* Option not present */
|
|
case EINVAL:
|
|
/* If it's not a valid list, try reading it as an atom,
|
|
* falling through to an error if it fails */
|
|
if (!xlu_cfg_replace_string(config, "serial",
|
|
&b_info->u.hvm.serial, 0))
|
|
break;
|
|
/* FALLTHRU */
|
|
default:
|
|
fprintf(stderr,"xl: Unable to parse serial.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
xlu_cfg_replace_string (config, "boot", &b_info->u.hvm.boot, 0);
|
|
xlu_cfg_get_defbool(config, "usb", &b_info->u.hvm.usb, 0);
|
|
if (!xlu_cfg_get_long (config, "usbversion", &l, 0))
|
|
b_info->u.hvm.usbversion = l;
|
|
switch (xlu_cfg_get_list_as_string_list(config, "usbdevice",
|
|
&b_info->u.hvm.usbdevice_list,
|
|
1))
|
|
{
|
|
|
|
case 0: break; /* Success */
|
|
case ESRCH: break; /* Option not present */
|
|
case EINVAL:
|
|
/* If it's not a valid list, try reading it as an atom,
|
|
* falling through to an error if it fails */
|
|
if (!xlu_cfg_replace_string(config, "usbdevice",
|
|
&b_info->u.hvm.usbdevice, 0))
|
|
break;
|
|
/* FALLTHRU */
|
|
default:
|
|
fprintf(stderr,"xl: Unable to parse usbdevice.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
xlu_cfg_get_defbool(config, "vkb_device", &b_info->u.hvm.vkb_device, 0);
|
|
xlu_cfg_replace_string (config, "soundhw", &b_info->u.hvm.soundhw, 0);
|
|
xlu_cfg_get_defbool(config, "xen_platform_pci",
|
|
&b_info->u.hvm.xen_platform_pci, 0);
|
|
xlu_cfg_get_defbool(config, "xen_platform_pci_bar_uc",
|
|
&b_info->u.hvm.xen_platform_pci_bar_uc, 0);
|
|
|
|
if(b_info->u.hvm.vnc.listen
|
|
&& b_info->u.hvm.vnc.display
|
|
&& strchr(b_info->u.hvm.vnc.listen, ':') != NULL) {
|
|
fprintf(stderr,
|
|
"ERROR: Display specified both in vnclisten"
|
|
" and vncdisplay!\n");
|
|
exit (1);
|
|
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "vendor_device", &buf, 0)) {
|
|
libxl_vendor_device d;
|
|
|
|
e = libxl_vendor_device_from_string(buf, &d);
|
|
if (e) {
|
|
fprintf(stderr,
|
|
"xl: unknown vendor_device '%s'\n",
|
|
buf);
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
b_info->u.hvm.vendor_device = d;
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "gic_version", &buf, 1)) {
|
|
e = libxl_gic_version_from_string(buf, &b_info->arch_arm.gic_version);
|
|
if (e) {
|
|
fprintf(stderr,
|
|
"Unknown gic_version \"%s\" specified\n", buf);
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "tee", &buf, 1)) {
|
|
e = libxl_tee_type_from_string(buf, &b_info->tee);
|
|
if (e) {
|
|
fprintf(stderr,
|
|
"Unknown tee \"%s\" specified\n", buf);
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "sve", &buf, 1)) {
|
|
e = libxl_sve_type_from_string(buf, &b_info->arch_arm.sve_vl);
|
|
if (e) {
|
|
fprintf(stderr, "Unknown sve \"%s\" specified\n", buf);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_long (config, "nr_spis", &l, 0))
|
|
b_info->arch_arm.nr_spis = l;
|
|
|
|
xlu_cfg_get_defbool(config, "trap_unmapped_accesses",
|
|
&b_info->trap_unmapped_accesses, 0);
|
|
|
|
if (!xlu_cfg_get_string(config, "arm_sci", &buf, 1)) {
|
|
if (parse_arm_sci_config(config, &b_info->arch_arm.arm_sci, buf)) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
parse_vkb_list(config, d_config);
|
|
|
|
d_config->virtios = NULL;
|
|
d_config->num_virtios = 0;
|
|
parse_virtio_list(config, d_config);
|
|
|
|
xlu_cfg_get_defbool(config, "xend_suspend_evtchn_compat",
|
|
&c_info->xend_suspend_evtchn_compat, 0);
|
|
|
|
if (!xlu_cfg_get_defbool(config, "msr_relaxed",
|
|
&b_info->arch_x86.msr_relaxed, 0))
|
|
fprintf(stderr,
|
|
"WARNING: msr_relaxed will be removed in future versions.\n"
|
|
"If it fixes an issue you are having please report to "
|
|
"xen-devel@lists.xenproject.org.\n");
|
|
|
|
xlu_cfg_get_defbool(config, "vpmu", &b_info->vpmu, 0);
|
|
|
|
xlu_cfg_destroy(config);
|
|
}
|
|
|
|
/* Returns -1 on failure; the amount of memory on success. */
|
|
int64_t parse_mem_size_kb(const char *mem)
|
|
{
|
|
char *endptr;
|
|
int64_t kbytes;
|
|
|
|
kbytes = strtoll(mem, &endptr, 10);
|
|
|
|
if (strlen(endptr) > 1)
|
|
return -1;
|
|
|
|
switch (tolower((uint8_t)*endptr)) {
|
|
case 't':
|
|
kbytes <<= 10;
|
|
/* fallthrough */
|
|
case 'g':
|
|
kbytes <<= 10;
|
|
/* fallthrough */
|
|
case '\0':
|
|
case 'm':
|
|
kbytes <<= 10;
|
|
/* fallthrough */
|
|
case 'k':
|
|
break;
|
|
case 'b':
|
|
kbytes >>= 10;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return kbytes;
|
|
}
|
|
|
|
|
|
void split_string_into_string_list(const char *str,
|
|
const char *delim,
|
|
libxl_string_list *psl)
|
|
{
|
|
char *s, *saveptr;
|
|
const char *p;
|
|
libxl_string_list sl;
|
|
|
|
int i = 0, nr = 0;
|
|
|
|
s = strdup(str);
|
|
if (s == NULL) {
|
|
fprintf(stderr, "unable to allocate memory to split string\n");
|
|
exit(-1);
|
|
}
|
|
|
|
/* Count number of entries */
|
|
p = strtok_r(s, delim, &saveptr);
|
|
do {
|
|
nr++;
|
|
} while ((p = strtok_r(NULL, delim, &saveptr)));
|
|
|
|
free(s);
|
|
|
|
s = strdup(str);
|
|
|
|
sl = malloc((nr+1) * sizeof (char *));
|
|
if (sl == NULL) {
|
|
fprintf(stderr, "unable to allocate memory to split string\n");
|
|
exit(-1);
|
|
}
|
|
|
|
p = strtok_r(s, delim, &saveptr);
|
|
do {
|
|
assert(i < nr);
|
|
sl[i] = strdup(p);
|
|
i++;
|
|
} while ((p = strtok_r(NULL, delim, &saveptr)));
|
|
sl[i] = NULL;
|
|
|
|
*psl = sl;
|
|
|
|
free(s);
|
|
}
|
|
|
|
void trim(char_predicate_t predicate, const char *input, char **output)
|
|
{
|
|
const char *first, *after;
|
|
|
|
for (first = input;
|
|
*first && predicate((unsigned char)first[0]);
|
|
first++)
|
|
;
|
|
|
|
for (after = first + strlen(first);
|
|
after > first && predicate((unsigned char)after[-1]);
|
|
after--)
|
|
;
|
|
|
|
size_t len_nonnull = after - first;
|
|
char *result = xmalloc(len_nonnull + 1);
|
|
|
|
memcpy(result, first, len_nonnull);
|
|
result[len_nonnull] = 0;
|
|
|
|
*output = result;
|
|
}
|
|
|
|
int split_string_into_pair(const char *str, char delim, char **a, char **b,
|
|
char_predicate_t predicate)
|
|
{
|
|
char *s, *p, *aa = NULL, *bb = NULL;
|
|
int rc = 0;
|
|
|
|
s = xstrdup(str);
|
|
|
|
p = strchr(s, delim);
|
|
if (p == NULL) {
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
*p = 0;
|
|
if (predicate) {
|
|
trim(predicate, s, &aa);
|
|
} else {
|
|
aa = xstrdup(s);
|
|
}
|
|
p++;
|
|
if (!*p) {
|
|
rc = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
if (predicate) {
|
|
trim(predicate, p, &bb);
|
|
} else {
|
|
bb = xstrdup(p);
|
|
}
|
|
|
|
*a = aa;
|
|
aa = NULL;
|
|
*b = bb;
|
|
bb = NULL;
|
|
out:
|
|
free(s);
|
|
free(aa);
|
|
free(bb);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|