mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
This patch implements pci passthrough (hotplug and coldplug) in libxenlight, it also adds three new commands to xl: pci-attach, pci-detach and pci-list. Currently flr on a device is done writing to /sys/bus/pci/drivers/pciback/do_flr pciback do_flr is present in both XCI and XCP 2.6.27 kernels. Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
1165 lines
44 KiB
C
1165 lines
44 KiB
C
/*
|
|
* Copyright (C) 2009 Citrix Ltd.
|
|
* Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
|
|
* Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <sys/select.h>
|
|
#include <signal.h>
|
|
#include <unistd.h> /* for write, unlink and close */
|
|
#include "libxl.h"
|
|
#include "libxl_utils.h"
|
|
#include "libxl_internal.h"
|
|
#include "flexarray.h"
|
|
|
|
int libxl_ctx_init(struct libxl_ctx *ctx)
|
|
{
|
|
memset(ctx, 0, sizeof(struct libxl_ctx));
|
|
ctx->alloc_maxsize = 256;
|
|
ctx->alloc_ptrs = calloc(ctx->alloc_maxsize, sizeof(void *));
|
|
if (!ctx->alloc_ptrs)
|
|
return ERROR_NOMEM;
|
|
|
|
ctx->xch = xc_interface_open();
|
|
ctx->xsh = xs_daemon_open();
|
|
return 0;
|
|
}
|
|
|
|
int libxl_ctx_free(struct libxl_ctx *ctx)
|
|
{
|
|
libxl_free_all(ctx);
|
|
free(ctx->alloc_ptrs);
|
|
ctx->alloc_ptrs = NULL;
|
|
xc_interface_close(ctx->xch);
|
|
xs_daemon_close(ctx->xsh);
|
|
return 0;
|
|
}
|
|
|
|
int libxl_ctx_set_log(struct libxl_ctx *ctx, libxl_log_callback log_callback, void *log_data)
|
|
{
|
|
ctx->log_callback = log_callback;
|
|
ctx->log_userdata = log_data;
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int libxl_domain_make(struct libxl_ctx *ctx, libxl_domain_create_info *info,
|
|
uint32_t *domid)
|
|
{
|
|
int flags, ret, i;
|
|
char *uuid_string;
|
|
char *rw_paths[] = { "device" };
|
|
char *ro_paths[] = { "cpu", "memory", "device", "error", "drivers",
|
|
"control", "attr", "data", "messages" };
|
|
char *dom_path, *vm_path, *vss_path;
|
|
struct xs_permissions roperm[2];
|
|
struct xs_permissions rwperm[1];
|
|
xs_transaction_t t;
|
|
xen_domain_handle_t handle;
|
|
|
|
uuid_string = libxl_uuid_to_string(ctx, info->uuid);
|
|
if (!uuid_string) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "missing uuid");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
flags = info->hvm ? XEN_DOMCTL_CDF_hvm_guest : 0;
|
|
flags |= info->hap ? XEN_DOMCTL_CDF_hap : 0;
|
|
*domid = 0;
|
|
|
|
/* XXX handle has to be initialised here.
|
|
* info->uuid != xen_domain_handle_t
|
|
* See:
|
|
* http://www.opengroup.org/dce/info/draft-leach-uuids-guids-01.txt
|
|
* http://www.opengroup.org/onlinepubs/009629399/apdxa.htm
|
|
*
|
|
* A DCE 1.1 compatible source representation of UUIDs.
|
|
*
|
|
* struct uuid {
|
|
* uint32_t time_low;
|
|
* uint16_t time_mid;
|
|
* uint16_t time_hi_and_version;
|
|
* uint8_t clock_seq_hi_and_reserved;
|
|
* uint8_t clock_seq_low;
|
|
* uint8_t node[_UUID_NODE_LEN];
|
|
* };
|
|
*/
|
|
|
|
ret = xc_domain_create(ctx->xch, info->ssidref, handle, flags, domid);
|
|
if (ret < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "domain creation fail: %d", ret);
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
dom_path = libxl_xs_get_dompath(ctx, *domid);
|
|
vm_path = libxl_sprintf(ctx, "/vm/%s", uuid_string);
|
|
vss_path = libxl_sprintf(ctx, "/vss/%s", uuid_string);
|
|
if (!dom_path || !vm_path || !vss_path) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "cannot allocate create paths");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
roperm[0].id = 0;
|
|
roperm[0].perms = XS_PERM_NONE;
|
|
roperm[1].id = *domid;
|
|
roperm[1].perms = XS_PERM_READ;
|
|
rwperm[0].id = *domid;
|
|
rwperm[0].perms = XS_PERM_NONE;
|
|
|
|
retry_transaction:
|
|
t = xs_transaction_start(ctx->xsh);
|
|
xs_rm(ctx->xsh, t, dom_path);
|
|
xs_mkdir(ctx->xsh, t, dom_path);
|
|
xs_set_permissions(ctx->xsh, t, dom_path, roperm, ARRAY_SIZE(roperm));
|
|
|
|
xs_rm(ctx->xsh, t, vm_path);
|
|
xs_mkdir(ctx->xsh, t, vm_path);
|
|
xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
|
|
|
|
xs_rm(ctx->xsh, t, vss_path);
|
|
xs_mkdir(ctx->xsh, t, vss_path);
|
|
xs_set_permissions(ctx->xsh, t, vss_path, rwperm, ARRAY_SIZE(rwperm));
|
|
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vm", dom_path), vm_path, strlen(vm_path));
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vss", dom_path), vss_path, strlen(vss_path));
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/name", dom_path), info->name, strlen(info->name));
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rw_paths); i++) {
|
|
char *path = libxl_sprintf(ctx, "%s/%s", dom_path, rw_paths[i]);
|
|
xs_mkdir(ctx->xsh, t, path);
|
|
xs_set_permissions(ctx->xsh, t, path, rwperm, ARRAY_SIZE(rwperm));
|
|
libxl_free(ctx, path);
|
|
}
|
|
for (i = 0; i < ARRAY_SIZE(ro_paths); i++) {
|
|
char *path = libxl_sprintf(ctx, "%s/%s", dom_path, ro_paths[i]);
|
|
xs_mkdir(ctx->xsh, t, path);
|
|
xs_set_permissions(ctx->xsh, t, path, roperm, ARRAY_SIZE(roperm));
|
|
libxl_free(ctx, path);
|
|
}
|
|
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/uuid", vm_path), uuid_string, strlen(uuid_string));
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/name", vm_path), info->name, strlen(info->name));
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/image/ostype", vm_path), "hvm", strlen("hvm"));
|
|
|
|
libxl_xs_writev(ctx, t, dom_path, info->xsdata);
|
|
libxl_xs_writev(ctx, t, libxl_sprintf(ctx, "%s/platform", dom_path), info->platformdata);
|
|
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1);
|
|
|
|
if (!xs_transaction_end(ctx->xsh, t, 0))
|
|
if (errno == EAGAIN)
|
|
goto retry_transaction;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_domain_build(struct libxl_ctx *ctx, libxl_domain_build_info *info, uint32_t domid)
|
|
{
|
|
libxl_domain_build_state state;
|
|
char **vments = NULL, **localents = NULL;
|
|
|
|
memset(&state, '\0', sizeof(state));
|
|
|
|
build_pre(ctx, domid, info, &state);
|
|
if (info->hvm) {
|
|
build_hvm(ctx, domid, info, &state);
|
|
vments = libxl_calloc(ctx, 4, sizeof(char *));
|
|
vments[0] = libxl_sprintf(ctx, "rtc/timeoffset");
|
|
vments[1] = libxl_sprintf(ctx, "%s", (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "");
|
|
} else {
|
|
build_pv(ctx, domid, info, &state);
|
|
}
|
|
build_post(ctx, domid, info, &state, vments, localents);
|
|
return 0;
|
|
}
|
|
|
|
int libxl_domain_restore(struct libxl_ctx *ctx, libxl_domain_build_info *info,
|
|
uint32_t domid, int fd)
|
|
{
|
|
libxl_domain_build_state state;
|
|
char **vments = NULL, **localents = NULL;
|
|
|
|
memset(&state, '\0', sizeof(state));
|
|
|
|
build_pre(ctx, domid, info, &state);
|
|
restore_common(ctx, domid, info, &state, fd);
|
|
if (info->hvm) {
|
|
vments = libxl_calloc(ctx, 4, sizeof(char *));
|
|
vments[0] = libxl_sprintf(ctx, "rtc/timeoffset");
|
|
vments[1] = libxl_sprintf(ctx, "%s", (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "");
|
|
} else {
|
|
localents = libxl_calloc(ctx, 4 * 2, sizeof(char *));
|
|
localents[0] = libxl_sprintf(ctx, "serial/0/limit");
|
|
localents[1] = libxl_sprintf(ctx, "%d", 65536);
|
|
localents[2] = libxl_sprintf(ctx, "console/port");
|
|
localents[3] = libxl_sprintf(ctx, "%d", state.console_port);
|
|
localents[4] = libxl_sprintf(ctx, "console/ring-ref");
|
|
localents[5] = libxl_sprintf(ctx, "%ld", state.console_mfn);
|
|
}
|
|
build_post(ctx, domid, info, &state, vments, localents);
|
|
return 0;
|
|
}
|
|
|
|
struct libxl_dominfo * libxl_domain_list(struct libxl_ctx *ctx, int *nb_domain)
|
|
{
|
|
struct libxl_dominfo *ptr;
|
|
int index, i, ret, first_domain;
|
|
xc_domaininfo_t info[16];
|
|
int size = 16;
|
|
|
|
first_domain = 1;
|
|
index = 0;
|
|
ptr = libxl_calloc(ctx, size, sizeof(struct libxl_dominfo));
|
|
if (!ptr)
|
|
return NULL;
|
|
redo:
|
|
ret = xc_domain_getinfolist(ctx->xch, first_domain, 16, info);
|
|
for (i = 0; i < ret; i++) {
|
|
if (index == size) {
|
|
struct libxl_dominfo *ptr2;
|
|
|
|
ptr2 = libxl_calloc(ctx, size * 2, sizeof(struct libxl_dominfo));
|
|
if (!ptr2) {
|
|
libxl_free(ctx, ptr);
|
|
return NULL;
|
|
}
|
|
memcpy(ptr2, ptr, sizeof(struct libxl_dominfo) * size);
|
|
libxl_free(ctx, ptr);
|
|
ptr = ptr2;
|
|
size *= 2;
|
|
}
|
|
memcpy(ptr[index].uuid, info[i].handle, 16 * sizeof(uint8_t));
|
|
ptr[index].domid = info[i].domain;
|
|
first_domain = info[i].domain + 1;
|
|
index++;
|
|
}
|
|
if (ret == 16)
|
|
goto redo;
|
|
*nb_domain = index;
|
|
return ptr;
|
|
}
|
|
|
|
xc_dominfo_t * libxl_domain_infolist(struct libxl_ctx *ctx, int *nb_domain)
|
|
{
|
|
int index, first_domain;
|
|
xc_dominfo_t *info;
|
|
int size = 1024;
|
|
|
|
first_domain = 0;
|
|
index = 0;
|
|
info = (xc_dominfo_t *) libxl_calloc(ctx, size, sizeof(xc_dominfo_t));
|
|
if (!info) {
|
|
*nb_domain = 0;
|
|
return NULL;
|
|
}
|
|
*nb_domain = xc_domain_getinfo(ctx->xch, first_domain, 1024, info);
|
|
return info;
|
|
}
|
|
|
|
int libxl_domain_suspend(struct libxl_ctx *ctx, libxl_domain_suspend_info *info,
|
|
uint32_t domid, int fd)
|
|
{
|
|
int hvm = 1;
|
|
int live = 0;
|
|
int debug = 0;
|
|
char savesig[] = "XenSavedDomain\n";
|
|
|
|
write(fd, savesig, strlen(savesig));
|
|
|
|
core_suspend(ctx, domid, fd, hvm, live, debug);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int libxl_domain_pause(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
xc_domain_pause(ctx->xch, domid);
|
|
return 0;
|
|
}
|
|
|
|
int libxl_domain_unpause(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
xc_domain_unpause(ctx->xch, domid);
|
|
return 0;
|
|
}
|
|
|
|
static char *req_table[] = {
|
|
[0] = "poweroff",
|
|
[1] = "reboot",
|
|
[2] = "suspend",
|
|
[3] = "crash",
|
|
[4] = "halt",
|
|
};
|
|
|
|
int libxl_domain_shutdown(struct libxl_ctx *ctx, uint32_t domid, int req)
|
|
{
|
|
char *shutdown_path;
|
|
char *dom_path;
|
|
|
|
if (req > ARRAY_SIZE(req_table))
|
|
return ERROR_INVAL;
|
|
|
|
dom_path = libxl_xs_get_dompath(ctx, domid);
|
|
shutdown_path = libxl_sprintf(ctx, "%s/control/shutdown", dom_path);
|
|
|
|
xs_write(ctx->xsh, XBT_NULL, shutdown_path, req_table[req], strlen(req_table[req]));
|
|
if (/* hvm */ 0) {
|
|
unsigned long acpi_s_state = 0;
|
|
unsigned long pvdriver = 0;
|
|
xc_get_hvm_param(ctx->xch, domid, HVM_PARAM_ACPI_S_STATE, &acpi_s_state);
|
|
xc_get_hvm_param(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
|
|
if (!pvdriver && acpi_s_state != 0)
|
|
xc_domain_shutdown(ctx->xch, domid, req);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int libxl_destroy_device_model(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
char *pid;
|
|
int ret;
|
|
|
|
pid = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/%d/image/device-model-pid", domid));
|
|
if (!pid) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Couldn't find device model's pid\n");
|
|
return -1;
|
|
}
|
|
xs_rm(ctx->xsh, XBT_NULL, libxl_sprintf(ctx, "/local/domain/0/device-model/%d", domid));
|
|
|
|
ret = kill(atoi(pid), SIGHUP);
|
|
if (ret < 0 && errno == ESRCH) {
|
|
XL_LOG(ctx, XL_LOG_DEBUG, "Device Model already exited\n");
|
|
ret = 0;
|
|
} else if (ret == 0) {
|
|
XL_LOG(ctx, XL_LOG_DEBUG, "Device Model signaled\n");
|
|
ret = 0;
|
|
} else {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "kill %d returned %d errno=%d\n", atoi(pid), ret, errno);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int libxl_domain_destroy(struct libxl_ctx *ctx, uint32_t domid, int force)
|
|
{
|
|
char *dom_path, vm_path[41];
|
|
xen_uuid_t *uuid;
|
|
|
|
dom_path = libxl_xs_get_dompath(ctx, domid);
|
|
if (!dom_path) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "dompath doesn't exist for %d\n", domid);
|
|
return -1;
|
|
}
|
|
if (libxl_domid_to_uuid(ctx, &uuid, domid) < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "failed ot get uuid for %d\n", domid);
|
|
return -1;
|
|
}
|
|
if (libxl_device_pci_shutdown(ctx, domid) < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "pci shutdown failed for domid %d\n", domid);
|
|
xs_write(ctx->xsh, XBT_NULL,
|
|
libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid),
|
|
"shutdown", strlen("shutdown"));
|
|
if (xc_domain_pause(ctx->xch, domid) < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "xc_domain_pause failed for %d\n", domid);
|
|
return -1;
|
|
}
|
|
if (xc_domain_destroy(ctx->xch, domid) < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "xc_domain_destroy failed for %d\n", domid);
|
|
return -1;
|
|
}
|
|
if (libxl_devices_destroy(ctx, domid, force) < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "libxl_destroy_devices failed for %d\n", domid);
|
|
if (libxl_destroy_device_model(ctx, domid) < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "libxl_destroy_device_model failed for %d\n", domid);
|
|
if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
|
|
XL_LOG(ctx, XL_LOG_ERROR, "xs_rm failed for %s\n", dom_path);
|
|
snprintf(vm_path, sizeof(vm_path), "/vm/%s", libxl_uuid_to_string(ctx, uuid));
|
|
if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
|
|
XL_LOG(ctx, XL_LOG_ERROR, "xs_rm failed for %s\n", vm_path);
|
|
return 0;
|
|
}
|
|
|
|
static char ** libxl_build_device_model_args(struct libxl_ctx *ctx,
|
|
libxl_device_model_info *info,
|
|
libxl_device_nic *vifs,
|
|
int num_vifs)
|
|
{
|
|
int num = 0, i;
|
|
flexarray_t *dm_args;
|
|
dm_args = flexarray_make(16, 1);
|
|
if (!dm_args)
|
|
return NULL;
|
|
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "qemu-dm"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-d"));
|
|
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%d", info->domid));
|
|
|
|
if (info->dom_name) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-domain-name"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->dom_name));
|
|
}
|
|
if (info->videoram) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-videoram"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%d", info->videoram));
|
|
}
|
|
if (info->stdvga) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-std-vga"));
|
|
}
|
|
if (info->vnc || info->vncdisplay || info->vnclisten || info->vncunused) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-vnc"));
|
|
if (info->vncdisplay) {
|
|
if (info->vnclisten && strchr(info->vnclisten, ':') == NULL) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s:%d", info->vnclisten, info->vncdisplay));
|
|
} else {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "127.0.0.1:%d", info->vncdisplay));
|
|
}
|
|
} else if (info->vnclisten) {
|
|
if (strchr(info->vnclisten, ':') != NULL) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->vnclisten));
|
|
} else {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s:0", info->vnclisten));
|
|
}
|
|
} else {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "127.0.0.1:0"));
|
|
}
|
|
if (info->vncunused) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-vncunused"));
|
|
}
|
|
}
|
|
if (info->sdl || info->opengl) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-sdl"));
|
|
if (info->opengl) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-disable-opengl"));
|
|
}
|
|
}
|
|
if (info->keymap) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-k"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->keymap));
|
|
}
|
|
if (info->nographic && (!info->sdl && !info->vnc)) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-nographic"));
|
|
}
|
|
if (info->serial) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-serial"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->serial));
|
|
}
|
|
if (info->boot) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-boot"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->boot));
|
|
}
|
|
if (info->usb) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-usb"));
|
|
if (info->usbdevice) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-usbdevice"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->usbdevice));
|
|
}
|
|
}
|
|
if (info->apic) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-acpi"));
|
|
}
|
|
if (info->extra) {
|
|
int i = 0;
|
|
while (info->extra[i] != NULL) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s", info->extra[i]));
|
|
}
|
|
}
|
|
for (i = 0; i < num_vifs; i++) {
|
|
if (vifs[i].nictype == NICTYPE_IOEMU) {
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-net"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "nic,vlan=%d,macaddr=%s,model=%s",
|
|
vifs[i].devid, vifs[i].smac, vifs[i].model));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-net"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "tap,vlan=%d,ifname=%s,bridge=%s",
|
|
vifs[i].devid, vifs[i].ifname, vifs[i].bridge));
|
|
}
|
|
}
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "-M"));
|
|
flexarray_set(dm_args, num++, libxl_sprintf(ctx, "xenfv"));
|
|
flexarray_set(dm_args, num++, NULL);
|
|
|
|
return (char **) flexarray_contents(dm_args);
|
|
}
|
|
|
|
int libxl_create_device_model(struct libxl_ctx *ctx,
|
|
libxl_device_model_info *info,
|
|
libxl_device_nic *vifs, int num_vifs)
|
|
{
|
|
char *dom_path, *path, *logfile, *logfile_new;
|
|
char *kvs[3];
|
|
struct stat stat_buf;
|
|
int logfile_w, null, pid;
|
|
int i;
|
|
char **args;
|
|
|
|
args = libxl_build_device_model_args(ctx, info, vifs, num_vifs);
|
|
if (!args)
|
|
return ERROR_FAIL;
|
|
|
|
dom_path = libxl_xs_get_dompath(ctx, info->domid);
|
|
|
|
path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d", info->domid);
|
|
xs_mkdir(ctx->xsh, XBT_NULL, path);
|
|
|
|
logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log", info->dom_name);
|
|
if (stat(logfile, &stat_buf) == 0) {
|
|
/* file exists, rotate */
|
|
logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log.10", info->dom_name);
|
|
unlink(logfile);
|
|
for (i = 9; i > 0; i--) {
|
|
logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log.%d", info->dom_name, i);
|
|
logfile_new = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log.%d", info->dom_name, i + 1);
|
|
rename(logfile, logfile_new);
|
|
}
|
|
logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log", info->dom_name);
|
|
logfile_new = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log.1", info->dom_name);
|
|
rename(logfile, logfile_new);
|
|
}
|
|
logfile = libxl_sprintf(ctx, "/var/log/xen/qemu-dm-%s.log", info->dom_name);
|
|
logfile_w = open(logfile, O_WRONLY|O_CREAT, 0644);
|
|
null = open("/dev/null", O_RDONLY);
|
|
pid = libxl_exec(ctx, null, logfile_w, logfile_w, info->device_model, args);
|
|
close(null);
|
|
close(logfile_w);
|
|
|
|
kvs[0] = libxl_sprintf(ctx, "image/device-model-pid");
|
|
kvs[1] = libxl_sprintf(ctx, "%d", pid);
|
|
kvs[2] = NULL;
|
|
libxl_xs_writev(ctx, XBT_NULL, dom_path, kvs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int libxl_device_disk_add(struct libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk)
|
|
{
|
|
flexarray_t *front;
|
|
flexarray_t *back;
|
|
char *backend_type;
|
|
unsigned int boffset = 0;
|
|
unsigned int foffset = 0;
|
|
int devid;
|
|
libxl_device device;
|
|
|
|
front = flexarray_make(16, 1);
|
|
if (!front)
|
|
return ERROR_NOMEM;
|
|
back = flexarray_make(16, 1);
|
|
if (!back) /* leaks front if error */
|
|
return ERROR_NOMEM;
|
|
|
|
backend_type = device_disk_backend_type_of_phystype(disk->phystype);
|
|
devid = device_disk_dev_number(disk->virtpath);
|
|
|
|
device.backend_devid = devid;
|
|
device.backend_domid = disk->backend_domid;
|
|
device.devid = devid;
|
|
device.domid = disk->domid;
|
|
device.kind = DEVICE_VBD;
|
|
|
|
switch (disk->phystype) {
|
|
case PHYSTYPE_FILE:
|
|
return ERROR_NI; /* FIXME */
|
|
break;
|
|
case PHYSTYPE_PHY: {
|
|
int major, minor;
|
|
|
|
device_disk_major_minor(disk->virtpath, &major, &minor);
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "physical-device"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%x:%x", major, minor));
|
|
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "params"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s", disk->physpath));
|
|
|
|
device.backend_kind = DEVICE_VBD;
|
|
break;
|
|
}
|
|
case PHYSTYPE_AIO: case PHYSTYPE_QCOW: case PHYSTYPE_QCOW2: case PHYSTYPE_VHD:
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "params"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s:%s",
|
|
device_disk_string_of_phystype(disk->phystype), disk->physpath));
|
|
|
|
device.backend_kind = DEVICE_TAP;
|
|
break;
|
|
}
|
|
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "frontend-id"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", disk->domid));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "online"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "1"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "removable"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", (disk->unpluggable) ? 1 : 0));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "state"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "dev"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s", disk->virtpath));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "type"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s", backend_type));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "mode"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s", (disk->readwrite) ? "w" : "r"));
|
|
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "backend-id"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", disk->backend_domid));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "state"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", 1));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "virtual-device"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", devid));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "device-type"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%s", (disk->is_cdrom) ? "cdrom" : "disk"));
|
|
|
|
if (0 /* protocol != native*/) {
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "protocol"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "x86_32-abi")); /* hardcoded ! */
|
|
}
|
|
|
|
libxl_device_generic_add(ctx, &device,
|
|
libxl_xs_kvs_of_flexarray(ctx, back, boffset),
|
|
libxl_xs_kvs_of_flexarray(ctx, front, foffset));
|
|
/* leaks both flexarray here */
|
|
return 0;
|
|
}
|
|
|
|
int libxl_device_disk_clean_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
int libxl_device_disk_hard_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int libxl_device_nic_add(struct libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic)
|
|
{
|
|
flexarray_t *front;
|
|
flexarray_t *back;
|
|
unsigned int boffset = 0;
|
|
unsigned int foffset = 0;
|
|
libxl_device device;
|
|
|
|
front = flexarray_make(16, 1);
|
|
if (!front)
|
|
return ERROR_NOMEM;
|
|
back = flexarray_make(16, 1);
|
|
if (!back)
|
|
return ERROR_NOMEM;
|
|
|
|
device.backend_devid = nic->devid;
|
|
device.backend_domid = nic->backend_domid;
|
|
device.backend_kind = DEVICE_VIF;
|
|
device.devid = nic->devid;
|
|
device.domid = nic->domid;
|
|
device.kind = DEVICE_VIF;
|
|
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "frontend-id"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", nic->domid));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "online"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "1"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "state"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "script"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s", nic->script));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "mac"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
nic->mac[0], nic->mac[1], nic->mac[2],
|
|
nic->mac[3], nic->mac[4], nic->mac[5]));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "handle"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", nic->devid));
|
|
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "backend-id"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", nic->backend_domid));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "state"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", 1));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "handle"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", nic->devid));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "mac"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
nic->mac[0], nic->mac[1], nic->mac[2],
|
|
nic->mac[3], nic->mac[4], nic->mac[5]));
|
|
if (0 /* protocol != native*/) {
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "protocol"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "x86_32-abi")); /* hardcoded ! */
|
|
}
|
|
|
|
libxl_device_generic_add(ctx, &device,
|
|
libxl_xs_kvs_of_flexarray(ctx, back, boffset),
|
|
libxl_xs_kvs_of_flexarray(ctx, front, foffset));
|
|
|
|
/* FIXME: wait for plug */
|
|
return 0;
|
|
}
|
|
|
|
int libxl_device_nic_clean_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
int libxl_device_nic_hard_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int libxl_device_vkb_add(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
int libxl_device_vkb_clean_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
int libxl_device_vkb_hard_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int libxl_device_vfb_add(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
int libxl_device_vfb_clean_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
int libxl_device_vfb_hard_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
return ERROR_NI;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int libxl_device_pci_init(libxl_device_pci *pcidev, unsigned int domain,
|
|
unsigned int bus, unsigned int dev,
|
|
unsigned int func, unsigned int vdevfn)
|
|
{
|
|
pcidev->domain = domain;
|
|
pcidev->bus = bus;
|
|
pcidev->dev = dev;
|
|
pcidev->func = func;
|
|
pcidev->vdevfn = vdevfn;
|
|
return 0;
|
|
}
|
|
|
|
static int libxl_create_pci_backend(struct libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, int num)
|
|
{
|
|
flexarray_t *front;
|
|
flexarray_t *back;
|
|
unsigned int boffset = 0;
|
|
unsigned int foffset = 0;
|
|
libxl_device device;
|
|
int i;
|
|
|
|
front = flexarray_make(16, 1);
|
|
if (!front)
|
|
return ERROR_NOMEM;
|
|
back = flexarray_make(16, 1);
|
|
if (!back)
|
|
return ERROR_NOMEM;
|
|
|
|
XL_LOG(ctx, XL_LOG_DEBUG, "Creating pci backend\n");
|
|
|
|
/* add pci device */
|
|
device.backend_devid = 0;
|
|
device.backend_domid = 0;
|
|
device.backend_kind = DEVICE_PCI;
|
|
device.devid = 0;
|
|
device.domid = domid;
|
|
device.kind = DEVICE_PCI;
|
|
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "frontend-id"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", domid));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "online"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "1"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "state"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "domain"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%s", libxl_domid_to_name(ctx, domid)));
|
|
for (i = 0; i < num; i++) {
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "key-%d", i));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "dev-%d", i));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
|
|
if (pcidev->vdevfn) {
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "vdevfn-%d", i));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%x", pcidev->vdevfn));
|
|
}
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "opts-%d", i));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "msitranslate=%d,power_mgmt=%d", pcidev->msitranslate, pcidev->power_mgmt));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "state-%d", i));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
|
|
}
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "num_devs"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", num));
|
|
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "backend-id"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", 0));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "state"));
|
|
flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", 1));
|
|
|
|
libxl_device_generic_add(ctx, &device,
|
|
libxl_xs_kvs_of_flexarray(ctx, back, boffset),
|
|
libxl_xs_kvs_of_flexarray(ctx, front, foffset));
|
|
|
|
flexarray_free(back);
|
|
flexarray_free(front);
|
|
return 0;
|
|
}
|
|
|
|
static int libxl_device_pci_add_xenstore(struct libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
|
|
{
|
|
flexarray_t *back;
|
|
char *num_devs, *be_path;
|
|
int num = 0;
|
|
unsigned int boffset = 0;
|
|
xs_transaction_t t;
|
|
|
|
be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", xs_get_domain_path(ctx->xsh, 0), domid);
|
|
num_devs = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/num_devs", be_path));
|
|
if (!num_devs)
|
|
return libxl_create_pci_backend(ctx, domid, pcidev, 1);
|
|
|
|
if (!is_hvm(ctx, domid)) {
|
|
if (libxl_wait_for_backend(ctx, be_path, "4") < 0)
|
|
return -1;
|
|
}
|
|
|
|
back = flexarray_make(16, 1);
|
|
if (!back)
|
|
return ERROR_NOMEM;
|
|
|
|
XL_LOG(ctx, XL_LOG_DEBUG, "Adding new pci device to xenstore\n");
|
|
num = atoi(num_devs);
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "key-%d", num));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "dev-%d", num));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
|
|
if (pcidev->vdevfn) {
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "vdevfn-%d", num));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%x", pcidev->vdevfn));
|
|
}
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "opts-%d", num));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "msitranslate=%d,power_mgmt=%d", pcidev->msitranslate, pcidev->power_mgmt));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "state-%d", num));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "num_devs"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", num + 1));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "state"));
|
|
flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 7));
|
|
|
|
retry_transaction:
|
|
t = xs_transaction_start(ctx->xsh);
|
|
libxl_xs_writev(ctx, t, be_path,
|
|
libxl_xs_kvs_of_flexarray(ctx, back, boffset));
|
|
if (!xs_transaction_end(ctx->xsh, t, 0))
|
|
if (errno == EAGAIN)
|
|
goto retry_transaction;
|
|
|
|
flexarray_free(back);
|
|
return 0;
|
|
}
|
|
|
|
static int libxl_device_pci_remove_xenstore(struct libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
|
|
{
|
|
char *be_path, *num_devs_path, *num_devs, *xsdev;
|
|
int num, i;
|
|
xs_transaction_t t;
|
|
unsigned int domain = 0, bus = 0, dev = 0, func = 0;
|
|
|
|
be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", xs_get_domain_path(ctx->xsh, 0), domid);
|
|
num_devs_path = libxl_sprintf(ctx, "%s/num_devs", be_path);
|
|
num_devs = libxl_xs_read(ctx, XBT_NULL, num_devs_path);
|
|
if (!num_devs)
|
|
return -1;
|
|
num = atoi(num_devs);
|
|
if (num == 1) {
|
|
libxl_device_destroy(ctx, be_path, 1);
|
|
xs_rm(ctx->xsh, XBT_NULL, be_path);
|
|
return 0;
|
|
}
|
|
|
|
if (!is_hvm(ctx, domid)) {
|
|
if (libxl_wait_for_backend(ctx, be_path, "4") < 0) {
|
|
XL_LOG(ctx, XL_LOG_DEBUG, "pci backend at %s is not ready\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num; i++) {
|
|
xsdev = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/dev-%d", be_path, i));
|
|
sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
|
|
if (domain == pcidev->domain && bus == pcidev->bus &&
|
|
pcidev->dev == dev && pcidev->func == func) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == num) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Couldn't find the device on xenstore\n");
|
|
return -1;
|
|
}
|
|
|
|
retry_transaction:
|
|
t = xs_transaction_start(ctx->xsh);
|
|
libxl_xs_write(ctx, t, num_devs_path, "%d", num - 1);
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/state-%d", be_path, i), "6", strlen("6"));
|
|
xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/state", be_path), "7", strlen("7"));
|
|
if (!xs_transaction_end(ctx->xsh, t, 0))
|
|
if (errno == EAGAIN)
|
|
goto retry_transaction;
|
|
return 0;
|
|
}
|
|
|
|
int libxl_device_pci_add(struct libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
|
|
{
|
|
char path[50];
|
|
char *state, *vdevfn;
|
|
int rc, hvm;
|
|
|
|
/* TODO: check if the device can be assigned */
|
|
|
|
libxl_device_pci_flr(ctx, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
|
|
|
|
hvm = is_hvm(ctx, domid);
|
|
if (hvm) {
|
|
if (libxl_wait_for_device_model(ctx, domid, "running") < 0) {
|
|
return -1;
|
|
}
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/state", domid);
|
|
state = libxl_xs_read(ctx, XBT_NULL, path);
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/parameter", domid);
|
|
if (pcidev->vdevfn)
|
|
libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF_VDEVFN, pcidev->domain,
|
|
pcidev->bus, pcidev->dev, pcidev->func, pcidev->vdevfn);
|
|
else
|
|
libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF, pcidev->domain,
|
|
pcidev->bus, pcidev->dev, pcidev->func);
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/command", domid);
|
|
xs_write(ctx->xsh, XBT_NULL, path, "pci-ins", strlen("pci-ins"));
|
|
if (libxl_wait_for_device_model(ctx, domid, "pci-inserted") < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Device Model didn't respond in time\n");
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/parameter", domid);
|
|
vdevfn = libxl_xs_read(ctx, XBT_NULL, path);
|
|
sscanf(vdevfn + 2, "%x", &pcidev->vdevfn);
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/state", domid);
|
|
xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
|
|
} else {
|
|
char *sysfs_path = libxl_sprintf(ctx, "SYSFS_PCI_DEV/"PCI_BDF"/resource", pcidev->domain,
|
|
pcidev->bus, pcidev->dev, pcidev->func);
|
|
FILE *f = fopen(sysfs_path, "r");
|
|
unsigned int start = 0, end = 0, flags = 0, size = 0;
|
|
int irq = 0;
|
|
int i;
|
|
|
|
if (f == NULL) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Couldn't open %s\n", sysfs_path);
|
|
return -1;
|
|
}
|
|
for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
|
|
fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags);
|
|
size = end - start + 1;
|
|
if (start) {
|
|
if (flags & PCI_BAR_IO) {
|
|
rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1);
|
|
if (rc < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_domain_ioport_permission error 0x%x/0x%x: %d\n", start, size, rc);
|
|
} else {
|
|
rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
|
|
(size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1);
|
|
if (rc < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_domain_iomem_permission error 0x%x/0x%x: %d\n", start, size, rc);
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
sysfs_path = libxl_sprintf(ctx, "SYSFS_PCI_DEV/"PCI_BDF"/irq", pcidev->domain,
|
|
pcidev->bus, pcidev->dev, pcidev->func);
|
|
f = fopen(sysfs_path, "r");
|
|
if (f == NULL) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Couldn't open %s\n", sysfs_path);
|
|
goto out;
|
|
}
|
|
fscanf(f, "%u", &irq);
|
|
if (irq) {
|
|
rc = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
|
|
if (rc < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_physdev_map_pirq irq=%d: %d\n", irq, rc);
|
|
}
|
|
rc = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
|
|
if (rc < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_domain_irq_permission irq=%d: %d\n", irq, rc);
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
out:
|
|
if ((rc = xc_assign_device(ctx->xch, domid, pcidev->value)) < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_assign_device error %d\n", rc);
|
|
|
|
libxl_device_pci_add_xenstore(ctx, domid, pcidev);
|
|
return 0;
|
|
}
|
|
|
|
int libxl_device_pci_remove(struct libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
|
|
{
|
|
char path[50];
|
|
char *state;
|
|
int hvm, rc;
|
|
|
|
/* TODO: check if the device can be detached */
|
|
|
|
hvm = is_hvm(ctx, domid);
|
|
if (hvm) {
|
|
if (libxl_wait_for_device_model(ctx, domid, "running") < 0) {
|
|
return -1;
|
|
}
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/state", domid);
|
|
state = libxl_xs_read(ctx, XBT_NULL, path);
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/parameter", domid);
|
|
libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF, pcidev->domain,
|
|
pcidev->bus, pcidev->dev, pcidev->func);
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/command", domid);
|
|
xs_write(ctx->xsh, XBT_NULL, path, "pci-rem", strlen("pci-rem"));
|
|
if (libxl_wait_for_device_model(ctx, domid, "pci-removed") < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Device Model didn't respond in time\n");
|
|
return -1;
|
|
}
|
|
snprintf(path, sizeof(path), "/local/domain/0/device-model/%d/state", domid);
|
|
xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
|
|
} else {
|
|
char *sysfs_path = libxl_sprintf(ctx, "SYSFS_PCI_DEV/"PCI_BDF"/resource", pcidev->domain,
|
|
pcidev->bus, pcidev->dev, pcidev->func);
|
|
FILE *f = fopen(sysfs_path, "r");
|
|
unsigned int start = 0, end = 0, flags = 0, size = 0;
|
|
int irq = 0;
|
|
int i;
|
|
|
|
if (f == NULL) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Couldn't open %s\n", sysfs_path);
|
|
goto skip1;
|
|
}
|
|
for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
|
|
fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags);
|
|
size = end - start + 1;
|
|
if (start) {
|
|
if (flags & PCI_BAR_IO) {
|
|
rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0);
|
|
if (rc < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_domain_ioport_permission error 0x%x/0x%x: %d\n", start, size, rc);
|
|
} else {
|
|
rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
|
|
(size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0);
|
|
if (rc < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_domain_iomem_permission error 0x%x/0x%x: %d\n", start, size, rc);
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
skip1:
|
|
sysfs_path = libxl_sprintf(ctx, "SYSFS_PCI_DEV/"PCI_BDF"/irq", pcidev->domain,
|
|
pcidev->bus, pcidev->dev, pcidev->func);
|
|
f = fopen(sysfs_path, "r");
|
|
if (f == NULL) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Couldn't open %s\n", sysfs_path);
|
|
goto out;
|
|
}
|
|
fscanf(f, "%u", &irq);
|
|
if (irq) {
|
|
rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
|
|
if (rc < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_physdev_map_pirq irq=%d: %d\n", irq, rc);
|
|
}
|
|
rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
|
|
if (rc < 0) {
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_domain_irq_permission irq=%d: %d\n", irq, rc);
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
out:
|
|
libxl_device_pci_remove_xenstore(ctx, domid, pcidev);
|
|
|
|
libxl_device_pci_flr(ctx, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
|
|
|
|
if ((rc = xc_deassign_device(ctx->xch, domid, pcidev->value)) < 0)
|
|
XL_LOG(ctx, XL_LOG_ERROR, "Error: xc_deassign_device error %d\n", rc);
|
|
return 0;
|
|
}
|
|
|
|
libxl_device_pci *libxl_device_pci_list(struct libxl_ctx *ctx, uint32_t domid, int *num)
|
|
{
|
|
char *be_path, *num_devs, *xsdev, *xsvdevfn, *xsopts;
|
|
int n, i;
|
|
unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0;
|
|
libxl_device_pci *pcidevs;
|
|
|
|
be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", xs_get_domain_path(ctx->xsh, 0), domid);
|
|
num_devs = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/num_devs", be_path));
|
|
if (!num_devs) {
|
|
*num = 0;
|
|
return NULL;
|
|
}
|
|
n = atoi(num_devs);
|
|
pcidevs = (libxl_device_pci *) libxl_calloc(ctx, n, sizeof(libxl_device_pci));
|
|
*num = n;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
xsdev = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/dev-%d", be_path, i));
|
|
sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
|
|
xsvdevfn = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, i));
|
|
if (xsvdevfn)
|
|
vdevfn = strtol(xsvdevfn, (char **) NULL, 16);
|
|
libxl_device_pci_init(pcidevs + i, domain, bus, dev, func, vdevfn);
|
|
xsopts = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/opts-%d", be_path, i));
|
|
if (xsopts) {
|
|
char *saveptr;
|
|
char *p = strtok_r(xsopts, ",=", &saveptr);
|
|
do {
|
|
while (*p == ' ')
|
|
p++;
|
|
if (!strcmp(p, "msitranslate")) {
|
|
p = strtok_r(NULL, ",=", &saveptr);
|
|
pcidevs[i].msitranslate = atoi(p);
|
|
} else if (!strcmp(p, "power_mgmt")) {
|
|
p = strtok_r(NULL, ",=", &saveptr);
|
|
pcidevs[i].power_mgmt = atoi(p);
|
|
}
|
|
} while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL);
|
|
}
|
|
}
|
|
return pcidevs;
|
|
}
|
|
|
|
int libxl_device_pci_shutdown(struct libxl_ctx *ctx, uint32_t domid)
|
|
{
|
|
libxl_device_pci *pcidevs;
|
|
int num, i;
|
|
|
|
pcidevs = libxl_device_pci_list(ctx, domid, &num);
|
|
for (i = 0; i < num; i++) {
|
|
if (libxl_device_pci_remove(ctx, domid, pcidevs + i) < 0)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|