mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-22 13:37:22 +00:00
[porthole] initial implementation of the porthole device interface
This is known as 'introspection' in the gnif/qemu repo, it's name is not final, however porthole is more appropriate but also may not be the final name. Note: This branch is experiemental and may never be released if QEMU do not accept the patch for the new device upstream.
This commit is contained in:
parent
0e7e918e2c
commit
438548c427
25
porthole/CMakeLists.txt
Normal file
25
porthole/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(porthole LANGUAGES C)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${PROJECT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(porthole STATIC
|
||||||
|
src/porthole.c
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
add_subdirectory(src/windows)
|
||||||
|
target_link_libraries(porthole PRIVATE porthole-windows)
|
||||||
|
else()
|
||||||
|
add_subdirectory(src/linux)
|
||||||
|
target_link_libraries(porthole PRIVATE porthole-linux)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(porthole
|
||||||
|
INTERFACE
|
||||||
|
include
|
||||||
|
PRIVATE
|
||||||
|
src
|
||||||
|
)
|
85
porthole/include/porthole/device.h
Normal file
85
porthole/include/porthole/device.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||||
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
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 <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct PortholeDev *PortholeDev;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the porthole device
|
||||||
|
*
|
||||||
|
* @param handle The returned handle if successful, otherwise undefined
|
||||||
|
* @param vendor_id The subsystem vendor and device id to match
|
||||||
|
* @returns true on success
|
||||||
|
*
|
||||||
|
* If successful the handle must be closed to free resources when finished.
|
||||||
|
*/
|
||||||
|
bool porthole_dev_open(PortholeDev *handle, const uint32_t vendor_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the porthole devce
|
||||||
|
*
|
||||||
|
* @param handle The porthole device handle obtained from porthole_dev_open
|
||||||
|
*
|
||||||
|
* handle will be set to NULL and is no longer valid after calling this function.
|
||||||
|
*/
|
||||||
|
void porthole_dev_close(PortholeDev *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Share the provided buffer over the porthole device
|
||||||
|
*
|
||||||
|
* @param handle The porthole device
|
||||||
|
* @param type The type
|
||||||
|
* @param buffer The buffer to share
|
||||||
|
* @param size The size of the buffer
|
||||||
|
* @returns true on success
|
||||||
|
*
|
||||||
|
* This function locks the supplied buffer in RAM via the porthole device
|
||||||
|
* driver and is then shared with the device for use outside the guest.
|
||||||
|
*
|
||||||
|
* The type parameter is application defined and is sent along with the buffer
|
||||||
|
* to the client application for buffer type identification.
|
||||||
|
*
|
||||||
|
* If successful the byffer must be unlocked with `porthole_dev_unlock` before
|
||||||
|
* the buffer can be freed.
|
||||||
|
*
|
||||||
|
* This is an expensive operation, the idea is that you allocate fixed buffers
|
||||||
|
* and share them with the host at initialization.
|
||||||
|
*
|
||||||
|
* @note the driver is hard limited to 32 shares.
|
||||||
|
*/
|
||||||
|
bool porthole_dev_share(PortholeDev handle, const uint32_t type, void *buffer, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlock a previously shared buffer
|
||||||
|
*
|
||||||
|
* @param handle The porthole device
|
||||||
|
* @param buffer The buffer to unlock
|
||||||
|
* @param size The size of the buffer
|
||||||
|
* @returns true on success
|
||||||
|
*
|
||||||
|
* Unlocks a previously shared buffer. Once this has been done the buffer can
|
||||||
|
* be freed or re-used. The client application should no longer attempt to
|
||||||
|
* access this buffer as it may be paged out of RAM.
|
||||||
|
*
|
||||||
|
* Note that this is not strictly required as closing the device will cause
|
||||||
|
* the driver to cleanup any prior locked buffers.
|
||||||
|
*/
|
||||||
|
bool porthole_dev_unlock(PortholeDev handle, void *buffer, size_t size);
|
13
porthole/src/linux/CMakeLists.txt
Normal file
13
porthole/src/linux/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(porthole-linux LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(porthole-linux STATIC
|
||||||
|
device.c
|
||||||
|
)
|
||||||
|
|
||||||
|
#target_link_libraries(porthole-linux)
|
||||||
|
|
||||||
|
target_include_directories(porthole-linux
|
||||||
|
PRIVATE
|
||||||
|
src
|
||||||
|
)
|
54
porthole/src/msg.h
Normal file
54
porthole/src/msg.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||||
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
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 <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t id;
|
||||||
|
} __attribute__ ((packed)) MsgFd;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t fd_id;
|
||||||
|
uint64_t addr;
|
||||||
|
uint32_t size;
|
||||||
|
} __attribute__ ((packed)) MsgSegment;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t type;
|
||||||
|
} __attribute__ ((packed)) MsgFinish;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t msg;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
MsgFd fd;
|
||||||
|
MsgSegment segment;
|
||||||
|
MsgFinish finish;
|
||||||
|
} u;
|
||||||
|
} __attribute__ ((packed)) Msg;
|
||||||
|
|
||||||
|
#define INTRO_MSG_RESET 0x1
|
||||||
|
#define INTRO_MSG_FD 0x2
|
||||||
|
#define INTRO_MSG_SEGMENT 0x3
|
||||||
|
#define INTRO_MSG_FINISH 0x4
|
||||||
|
|
||||||
|
#define INTRO_MSG_RESET_SIZE (sizeof(uint32_t))
|
||||||
|
#define INTRO_MSG_FD_SIZE (sizeof(uint32_t) + sizeof(MsgFd))
|
||||||
|
#define INTRO_MSG_SEGMENT_SIZE (sizeof(uint32_t) + sizeof(MsgSegment))
|
||||||
|
#define INTRO_MSG_FINISH_SIZE (sizeof(uint32_t) + sizeof(MsgFinish))
|
18
porthole/src/porthole.c
Normal file
18
porthole/src/porthole.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||||
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
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
|
||||||
|
*/
|
16
porthole/src/windows/CMakeLists.txt
Normal file
16
porthole/src/windows/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(porthole-windows LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(porthole-windows STATIC
|
||||||
|
device.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(porthole-windows
|
||||||
|
setupapi
|
||||||
|
lg_common
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(porthole-windows
|
||||||
|
PRIVATE
|
||||||
|
src
|
||||||
|
)
|
158
porthole/src/windows/device.c
Normal file
158
porthole/src/windows/device.c
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||||
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
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 "porthole/device.h"
|
||||||
|
#include "driver.h"
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <setupapi.h>
|
||||||
|
|
||||||
|
struct PortholeDev
|
||||||
|
{
|
||||||
|
HANDLE dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool porthole_dev_open(PortholeDev *handle, const uint32_t vendor_id)
|
||||||
|
{
|
||||||
|
HDEVINFO devInfo = {0};
|
||||||
|
PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL;
|
||||||
|
SP_DEVICE_INTERFACE_DATA devInfData = {0};
|
||||||
|
HANDLE dev;
|
||||||
|
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Invalid buffer provided");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
devInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
||||||
|
devInfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||||
|
|
||||||
|
for(int devIndex = 0; ; ++devIndex)
|
||||||
|
{
|
||||||
|
if (SetupDiEnumDeviceInterfaces(devInfo, NULL, &GUID_DEVINTERFACE_PORTHOLE, devIndex, &devInfData) == FALSE)
|
||||||
|
{
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (error == ERROR_NO_MORE_ITEMS)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Unable to enumerate the device, is it attached?");
|
||||||
|
SetupDiDestroyDeviceInfoList(devInfo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD reqSize = 0;
|
||||||
|
SetupDiGetDeviceInterfaceDetail(devInfo, &devInfData, NULL, 0, &reqSize, NULL);
|
||||||
|
if (!reqSize)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("SetupDiGetDeviceInterfaceDetail for %lu failed\n", reqSize);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(reqSize, 1);
|
||||||
|
infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||||
|
if (!SetupDiGetDeviceInterfaceDetail(devInfo, &devInfData, infData, reqSize, NULL, NULL))
|
||||||
|
{
|
||||||
|
free(infData);
|
||||||
|
DEBUG_WARN("SetupDiGetDeviceInterfaceDetail for %lu failed\n", reqSize);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the subsys id from the device */
|
||||||
|
unsigned int vendorID, deviceID, subsysID;
|
||||||
|
if (sscanf(infData->DevicePath, "\\\\?\\pci#ven_%4x&dev_%4x&subsys_%8x", &vendorID, &deviceID, &subsysID) != 3)
|
||||||
|
{
|
||||||
|
free(infData);
|
||||||
|
DEBUG_ERROR("Failed to parse: %s", infData->DevicePath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subsysID != vendor_id)
|
||||||
|
{
|
||||||
|
DEBUG_INFO("Skipping device %d, vendor_id 0x%x != 0x%x", devIndex, subsysID, vendor_id);
|
||||||
|
free(infData);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0);
|
||||||
|
if (dev == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to open device %d", devIndex);
|
||||||
|
free(infData);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_INFO("Device found");
|
||||||
|
|
||||||
|
free(infData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*handle = (PortholeDev)calloc(sizeof(struct PortholeDev), 1);
|
||||||
|
if (!*handle)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to allocate PortholeDev struct, out of memory!");
|
||||||
|
CloseHandle(dev);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*handle)->dev = dev;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void porthole_dev_close(PortholeDev *handle)
|
||||||
|
{
|
||||||
|
CloseHandle((*handle)->dev);
|
||||||
|
free(*handle);
|
||||||
|
*handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool porthole_dev_share(PortholeDev handle, const uint32_t type, void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
DWORD returned;
|
||||||
|
|
||||||
|
PortholeMsg msg = {
|
||||||
|
.type = type,
|
||||||
|
.addr = buffer,
|
||||||
|
.size = size
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!DeviceIoControl(handle->dev, IOCTL_PORTHOLE_SEND_MSG, &msg, sizeof(PortholeMsg), NULL, 0, &returned, NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool porthole_dev_unlock(PortholeDev handle, void *buffer, size_t size)
|
||||||
|
{
|
||||||
|
DWORD returned;
|
||||||
|
|
||||||
|
PortholeLockMsg msg = {
|
||||||
|
.addr = buffer,
|
||||||
|
.size = size
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!DeviceIoControl(handle->dev, IOCTL_PORTHOLE_UNLOCK_BUFFER, &msg , sizeof(PortholeLockMsg), NULL, 0, &returned, NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
24
porthole/src/windows/driver.h
Normal file
24
porthole/src/windows/driver.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
#include <initguid.h>
|
||||||
|
|
||||||
|
DEFINE_GUID (GUID_DEVINTERFACE_PORTHOLE,
|
||||||
|
0x10ccc0ac,0xf4b0,0x4d78,0xba,0x41,0x1e,0xbb,0x38,0x5a,0x52,0x85);
|
||||||
|
// {10ccc0ac-f4b0-4d78-ba41-1ebb385a5285}
|
||||||
|
|
||||||
|
typedef struct _PortholeMsg
|
||||||
|
{
|
||||||
|
UINT32 type;
|
||||||
|
PVOID addr;
|
||||||
|
UINT32 size;
|
||||||
|
}
|
||||||
|
PortholeMsg, *PPortholeMsg;
|
||||||
|
|
||||||
|
typedef struct _PortholeLockMsg
|
||||||
|
{
|
||||||
|
PVOID addr;
|
||||||
|
UINT32 size;
|
||||||
|
}
|
||||||
|
PortholeLockMsg, *PPortholeLockMsg;
|
||||||
|
|
||||||
|
#define IOCTL_PORTHOLE_SEND_MSG CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_PORTHOLE_UNLOCK_BUFFER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
Loading…
Reference in New Issue
Block a user