/* Looking Glass - KVM FrameRelay (KVMFR) Client Copyright (C) 2017-2020 Geoffrey McRae https://looking-glass.hostfission.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common/ivshmem.h" #include #include #include #include #include #include #include #include #include #include #include "common/debug.h" #include "common/option.h" #include "common/stringutils.h" #include "module/kvmfr.h" struct IVSHMEMInfo { int devFd; int dmaFd; int size; }; static bool ivshmemDeviceValidator(struct Option * opt, const char ** error) { // if it's not a kvmfr device, it must be a file on disk if (strlen(opt->value.x_string) > 3 && memcmp(opt->value.x_string, "kvmfr", 5) != 0) { struct stat st; if (stat(opt->value.x_string, &st) != 0) { *error = "Invalid path to the ivshmem file specified"; return false; } return true; } return true; } static StringList ivshmemDeviceGetValues(struct Option * option) { StringList sl = stringlist_new(true); DIR * d = opendir("/sys/class/kvmfr"); if (!d) return sl; struct dirent * dir; while((dir = readdir(d)) != NULL) { if (dir->d_name[0] == '.') continue; char * devName; alloc_sprintf(&devName, "/dev/%s", dir->d_name); stringlist_push(sl, devName); } closedir(d); return sl; } void ivshmemOptionsInit() { struct Option options[] = { { .module = "app", .name = "shmFile", .shortopt = 'f', .description = "The path to the shared memory file, or the name of the kvmfr device to use, ie: kvmfr0", .type = OPTION_TYPE_STRING, .value.x_string = "/dev/shm/looking-glass", .validator = ivshmemDeviceValidator, .getValues = ivshmemDeviceGetValues }, {0} }; option_register(options); } bool ivshmemOpen(struct IVSHMEM * dev) { return ivshmemOpenDev(dev, option_get_string("app", "shmFile")); } bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDevice) { assert(dev); unsigned int devSize; int devFd = -1; int dmaFd = -1; int mapFd = -1; dev->opaque = NULL; DEBUG_INFO("KVMFR Device : %s", shmDevice); if (strlen(shmDevice) > 8 && memcmp(shmDevice, "/dev/kvmfr", 10) == 0) { devFd = open(shmDevice, O_RDWR, (mode_t)0600); if (devFd < 0) { DEBUG_ERROR("Failed to open: %s", shmDevice); DEBUG_ERROR("%s", strerror(errno)); return false; } // get the device size devSize = ioctl(devFd, KVMFR_DMABUF_GETSIZE, 0); const struct kvmfr_dmabuf_create create = { .flags = KVMFR_DMABUF_FLAG_CLOEXEC, .offset = 0x0, .size = devSize }; dmaFd = ioctl(devFd, KVMFR_DMABUF_CREATE, &create); if (dmaFd < 0) { DEBUG_ERROR("Failed to create the dma buffer"); close(devFd); return false; } mapFd = dmaFd; } else { struct stat st; if (stat(shmDevice, &st) != 0) { DEBUG_ERROR("Failed to stat: %s", shmDevice); DEBUG_ERROR("%s", strerror(errno)); return false; } devSize = st.st_size; devFd = open(shmDevice, O_RDWR, (mode_t)0600); if (devFd < 0) { DEBUG_ERROR("Failed to open: %s", shmDevice); DEBUG_ERROR("%s", strerror(errno)); return false; } mapFd = devFd; } void * map = mmap(0, devSize, PROT_READ | PROT_WRITE, MAP_SHARED, mapFd, 0); if (map == MAP_FAILED) { DEBUG_ERROR("Failed to map the shared memory device: %s", shmDevice); DEBUG_ERROR("%s", strerror(errno)); return false; } struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)malloc(sizeof(struct IVSHMEMInfo)); info->size = devSize; info->devFd = devFd; info->dmaFd = dmaFd; dev->opaque = info; dev->size = devSize; dev->mem = map; return true; } void ivshmemClose(struct IVSHMEM * dev) { assert(dev); if (!dev->opaque) return; struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)dev->opaque; munmap(dev->mem, info->size); if (info->dmaFd >= 0) close(info->dmaFd); close(info->devFd); free(info); dev->mem = NULL; dev->size = 0; dev->opaque = NULL; } bool ivshmemHasDMA(struct IVSHMEM * dev) { assert(dev && dev->opaque); struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)dev->opaque; return info->dmaFd >= 0; } int ivshmemGetDMABuf(struct IVSHMEM * dev, uint64_t offset, uint64_t size) { assert(ivshmemHasDMA(dev)); assert(dev && dev->opaque); assert(offset + size <= dev->size); struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)dev->opaque; // align to the page size size = (size & ~(0x1000-1)) + 0x1000; const struct kvmfr_dmabuf_create create = { .flags = KVMFR_DMABUF_FLAG_CLOEXEC, .offset = offset, .size = size }; int fd = ioctl(info->devFd, KVMFR_DMABUF_CREATE, &create); if (fd < 0) DEBUG_ERROR("Failed to create the dma buffer"); return fd; }