Compare commits

...

28 Commits

Author SHA1 Message Date
Geoffrey McRae
ce5c36705e [porthole] fix invalid pointer dereference 2019-11-12 16:51:04 +11:00
Geoffrey McRae
7aee4eed11 [porthole] added missing file and adjusted linux code 2019-11-12 15:55:13 +11:00
Geoffrey McRae
416c4bbb48 [porthole] added missing include 2019-11-12 15:50:12 +11:00
Geoffrey McRae
76007092d5 [porthole] make the segments private and add a method to obtain the ptr 2019-11-12 15:43:44 +11:00
Geoffrey McRae
453b8e6a4d [porthole] added connection state support 2019-11-12 15:18:53 +11:00
Geoffrey McRae
968b313993 [porthole] updated windows driver defines 2019-11-12 13:08:37 +11:00
Geoffrey McRae
4dde82c646 [porthole] process all messages sent from qemu 2019-11-05 14:56:07 +11:00
Geoffrey McRae
4c424cdbdf [porthole] fix unmap control flow 2019-11-05 01:30:33 +11:00
Geoffrey McRae
963e7f8393 [porthole] prevent double include of headers 2019-11-05 01:09:11 +11:00
Geoffrey McRae
f93c918aa5 [porthole] added memory copy utility functions 2019-11-05 00:50:39 +11:00
Geoffrey McRae
d5f409b02e [common] add include for abort to debug.h 2019-11-05 00:20:05 +11:00
Geoffrey McRae
75cea21cfc [porthole] added receive timeout to allow for clean shutdown 2019-11-04 23:24:34 +11:00
Geoffrey McRae
df2a3b6151 [porthole] bug fixes 2019-11-04 23:07:26 +11:00
Geoffrey McRae
fad4d18973 [porthole] added missing header 2019-11-04 22:42:17 +11:00
Geoffrey McRae
f4ad730cc4 [arbiter] initial arbiter program for porthole communications 2019-11-04 22:39:27 +11:00
Geoffrey McRae
67ddb70932 [porthole] link pthreads and fix function type 2019-11-04 22:25:19 +11:00
Geoffrey McRae
27c3a93d15 [porthole] added unmap logic and response 2019-11-04 22:05:50 +11:00
Geoffrey McRae
df9798c819 [common] added objectlist_pop and objectlist_remove methods 2019-11-04 22:05:21 +11:00
Geoffrey McRae
1dfa0ed218 [common] added missing file to the repository 2019-11-04 21:10:21 +11:00
Geoffrey McRae
01f5238a9d [porthole] initial client implementation 2019-11-04 21:09:13 +11:00
Geoffrey McRae
c382a5acb1 [common] objectlists store void* not char* 2019-11-04 21:08:29 +11:00
Geoffrey McRae
5e3a46beb9 [common] add DEBUG_FATAL 2019-11-04 21:08:17 +11:00
Geoffrey McRae
6ed4e23b80 [common] fix objectlist_push type 2019-11-04 17:41:12 +11:00
Geoffrey McRae
0851ae6f14 [common] converted stringlist to a generic objectlist 2019-11-04 16:41:57 +11:00
Geoffrey McRae
caebddce4d [porthole] cosmetics, remove tabs 2019-10-31 23:46:46 +11:00
Geoffrey McRae
01da541815 [porthole] update in accordance with the recent windows driver changes 2019-10-31 23:45:08 +11:00
Geoffrey McRae
9d6bb57eff [porthole] cosmetics: remove tabs 2019-10-30 17:39:27 +11:00
Geoffrey McRae
438548c427 [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.
2019-10-30 17:28:13 +11:00
24 changed files with 1653 additions and 88 deletions

View File

@@ -1 +1 @@
B1-16-g7d6e061ade+1
B1-43-g83047cbc3e+1

3
arbiter/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
bin/
build/
*.swp

62
arbiter/CMakeLists.txt Normal file
View File

@@ -0,0 +1,62 @@
cmake_minimum_required(VERSION 3.0)
project(looking-glass-arbiter C)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
include(GNUInstallDirs)
include(CheckCCompilerFlag)
include(FeatureSummary)
option(OPTIMIZE_FOR_NATIVE "Build with -march=native" ON)
if(OPTIMIZE_FOR_NATIVE)
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
if(COMPILER_SUPPORTS_MARCH_NATIVE)
add_compile_options("-march=native")
endif()
endif()
add_compile_options(
"-Wall"
"-Werror"
"-Wfatal-errors"
"-ffast-math"
"-fdata-sections"
"-ffunction-sections"
"$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
)
set(EXE_FLAGS "-Wl,--gc-sections")
set(CMAKE_C_STANDARD 11)
execute_process(
COMMAND cat ../VERSION
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE BUILD_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions(-D BUILD_VERSION='"${BUILD_VERSION}"')
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
include_directories(
${PROJECT_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
)
set(SOURCES
src/main.c
)
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common")
add_subdirectory("${PROJECT_TOP}/porthole" "${CMAKE_BINARY_DIR}/porthole")
add_executable(looking-glass-arbiter ${SOURCES})
target_link_libraries(looking-glass-arbiter
${EXE_FLAGS}
lg_common
porthole
)
install(PROGRAMS ${CMAKE_BINARY_DIR}/looking-glass-arbiter DESTINATION bin/ COMPONENT binary)
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)

55
arbiter/src/main.c Normal file
View File

@@ -0,0 +1,55 @@
#include "common/debug.h"
#include "common/option.h"
#include "porthole/client.h"
static struct Option options[] =
{
// app options
{
.module = "host",
.name = "socket",
.description = "The porthole host socket",
.type = OPTION_TYPE_STRING,
.value.x_string = "/var/tmp/porthole",
},
{0}
};
static void map_event(uint32_t type, PortholeMap * map)
{
DEBUG_INFO("map_event: %u, %u, %u", type, map->id, map->size);
}
static void unmap_event(uint32_t id)
{
DEBUG_INFO("unmap_event: %u", id);
}
static void discon_event()
{
DEBUG_INFO("discon_event");
}
int main(int argc, char * argv[])
{
option_register(options);
if (!option_parse(argc, argv))
return -1;
if (!option_validate())
return -1;
PortholeClient phc;
if (!porthole_client_open(
&phc,
option_get_string("host", "socket"),
map_event,
unmap_event,
discon_event))
{
return -1;
}
porthole_client_close(&phc);
return 0;
}

View File

@@ -10,8 +10,8 @@ if(ENABLE_BACKTRACE)
endif()
set(COMMON_SOURCES
src/objectlist.c
src/stringutils.c
src/stringlist.c
src/option.c
src/framebuffer.c
)

View File

@@ -18,6 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32) && !defined(__GNUC__)
#define DIRECTORY_SEPARATOR '\\'
@@ -53,6 +54,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#define DEBUG_WARN(fmt, ...) DEBUG_PRINT("[W]", fmt, ##__VA_ARGS__)
#define DEBUG_ERROR(fmt, ...) DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
#define DEBUG_FIXME(fmt, ...) DEBUG_PRINT("[F]", fmt, ##__VA_ARGS__)
#define DEBUG_FATAL(fmt, ...) do {DEBUG_PRINT("[C]", fmt, ##__VA_ARGS__); abort();} while (0)
#if defined(DEBUG_SPICE) | defined(DEBUG_IVSHMEM)
#define DEBUG_PROTO(fmt, args...) DEBUG_PRINT("[P]", fmt, ##args)

View File

@@ -0,0 +1,35 @@
/*
KVMGFX Client - A KVM Client for VGA Passthrough
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>
typedef struct ObjectList * ObjectList;
typedef void (*ObjectFreeFn)(void * object);
ObjectList objectlist_new (ObjectFreeFn free_fn);
void objectlist_free (ObjectList * ol);
int objectlist_push (ObjectList ol, void * object);
void * objectlist_pop (ObjectList ol);
bool objectlist_remove(ObjectList ol, unsigned int index);
unsigned int objectlist_count (ObjectList ol);
void * objectlist_at (ObjectList ol, unsigned int index);
// generic free method
void objectlist_free_item(void *object);

View File

@@ -17,12 +17,31 @@ 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 "objectlist.h"
typedef struct StringList * StringList;
typedef ObjectList StringList;
StringList stringlist_new (bool owns_strings);
void stringlist_free (StringList * sl);
int stringlist_push (StringList sl, char * str);
unsigned int stringlist_count(StringList sl);
char * stringlist_at (StringList sl, unsigned int index);
inline static StringList stringlist_new(bool owns_strings)
{
return objectlist_new(owns_strings ? objectlist_free_item : 0);
}
inline static void stringlist_free(StringList * sl)
{
return objectlist_free(sl);
}
inline static int stringlist_push (StringList sl, char * str)
{
return objectlist_push(sl, str);
}
inline static unsigned int stringlist_count(StringList sl)
{
return objectlist_count(sl);
}
inline static char * stringlist_at(StringList sl, unsigned int index)
{
return objectlist_at(sl, index);
}

112
common/src/objectlist.c Normal file
View File

@@ -0,0 +1,112 @@
/*
KVMGFX Client - A KVM Client for VGA Passthrough
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 "common/objectlist.h"
#include <stdlib.h>
#include <string.h>
struct ObjectList
{
ObjectFreeFn free_fn;
unsigned int size;
unsigned int count;
void ** list;
};
ObjectList objectlist_new(ObjectFreeFn free_fn)
{
ObjectList ol = malloc(sizeof(struct ObjectList));
ol->free_fn = free_fn;
ol->size = 32;
ol->count = 0;
ol->list = malloc(sizeof(void *) * ol->size);
return ol;
}
void objectlist_free(ObjectList * ol)
{
if ((*ol)->free_fn)
for(unsigned int i = 0; i < (*ol)->count; ++i)
(*ol)->free_fn((*ol)->list[i]);
free((*ol)->list);
free((*ol));
*ol = NULL;
}
int objectlist_push(ObjectList ol, void * object)
{
if (ol->count == ol->size)
{
ol->size += 32;
ol->list = realloc(ol->list, sizeof(void *) * ol->size);
}
unsigned int index = ol->count;
ol->list[ol->count++] = object;
return index;
}
void * objectlist_pop(ObjectList ol)
{
if (ol->count == 0)
return NULL;
return ol->list[--ol->count];
}
bool objectlist_remove(ObjectList ol, unsigned int index)
{
if (index >= ol->count)
return false;
if (ol->free_fn)
ol->free_fn(ol->list[index]);
void ** newlist = malloc(sizeof(void *) * ol->size);
--ol->count;
memcpy(&newlist[0], &ol->list[0], index * sizeof(void *));
memcpy(&newlist[index], &ol->list[index + 1], (ol->count - index) * sizeof(void *));
free(ol->list);
ol->list = newlist;
return true;
}
unsigned int objectlist_count(ObjectList ol)
{
return ol->count;
}
void * objectlist_at(ObjectList ol, unsigned int index)
{
if (index >= ol->count)
return NULL;
return ol->list[index];
}
void objectlist_free_item(void *object)
{
free(object);
}

View File

@@ -1,79 +0,0 @@
/*
KVMGFX Client - A KVM Client for VGA Passthrough
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 "common/stringlist.h"
#include <stdlib.h>
struct StringList
{
bool owns_strings;
unsigned int size;
unsigned int count;
char ** list;
};
StringList stringlist_new(bool owns_strings)
{
StringList sl = malloc(sizeof(struct StringList));
sl->owns_strings = owns_strings;
sl->size = 32;
sl->count = 0;
sl->list = malloc(sizeof(char *) * sl->size);
return sl;
}
void stringlist_free(StringList * sl)
{
if ((*sl)->owns_strings)
for(unsigned int i = 0; i < (*sl)->count; ++i)
free((*sl)->list[i]);
free((*sl)->list);
free((*sl));
*sl = NULL;
}
int stringlist_push (StringList sl, char * str)
{
if (sl->count == sl->size)
{
sl->size += 32;
sl->list = realloc(sl->list, sizeof(char *) * sl->size);
}
unsigned int index = sl->count;
sl->list[sl->count++] = str;
return index;
}
unsigned int stringlist_count(StringList sl)
{
return sl->count;
}
char * stringlist_at(StringList sl, unsigned int index)
{
if (index >= sl->count)
return NULL;
return sl->list[index];
}

33
porthole/CMakeLists.txt Normal file
View File

@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.0)
project(porthole LANGUAGES C)
include_directories(
${PROJECT_SOURCE_DIR}/include
)
add_library(porthole STATIC
src/porthole.c
src/util.c
)
if(WIN32)
add_subdirectory(src/windows)
set(PLATFORM "windows")
else()
add_subdirectory(src/linux)
set(PLATFORM "linux")
endif()
target_link_libraries(porthole
PRIVATE
porthole-${PLATFORM}
lg_common
pthread
)
target_include_directories(porthole
INTERFACE
include
PRIVATE
src
)

View File

@@ -0,0 +1,81 @@
/*
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
*/
#pragma once
#include "types.h"
typedef struct PortholeClient *PortholeClient;
/**
* Memory map event callback.
*
* @param type The type ID provided by the guest
* @param map The new mapping
*
* @note this is called from the socket thread
*/
typedef void (*PortholeMapEvent)(uint32_t type, PortholeMap * map);
/**
* Memory unmap event callback.
*
* @param id The id of the mapping that has been unmapped by the guest
*
* @note this is called from the socket thread
*/
typedef void (*PortholeUnmapEvent)(uint32_t id);
/**
* Unexpected client disconnection event.
*
* When this occurs all mappings become invalid and should no longer be used.
*
* @note this is called from the socket thread
*/
typedef void (*PortholeDisconEvent)();
/**
* Open the porthole device
*
* @param handle Returned handle if successful, otherwise undefined
* @param socket_path Path to the unix socket of the porthole char device
* @param map_event Callback for map events from the guest
* @param unmap_event Callback for unmap events from the guest
* @param discon_event Callback for client socket disconnection
* @returns true on success
*
* If successful the handle must be closed to free resources when finished.
*/
bool porthole_client_open(
PortholeClient * handle,
const char * socket_path,
PortholeMapEvent map_cb,
PortholeUnmapEvent unmap_cb,
PortholeDisconEvent discon_cb);
/**
* Close the porthole devce
*
* @param handle The porthole client handle obtained from porthole_client_open
*
* handle will be set to NULL and is no longer valid after calling this
* function and all mappings will become invalid.
*/
void porthole_client_close(PortholeClient * handle);

View File

@@ -0,0 +1,123 @@
/*
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
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef struct PortholeDev *PortholeDev;
typedef int PortholeID;
typedef enum PortholeState
{
PH_STATE_NEW_SESSION,
PH_STATE_CONNECTED,
PH_STATE_DISCONNECTED
}
PortholeState;
/**
* Open the porthole device
*
* @param handle The returned handle if successful, otherwise undefined
* @param vendor_id The subsystem vendor and device id to match
* @return 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);
/**
* Get the state of the porthole device
*
* @param handle The porthole device handle obtained form porthole_dev_open
* @return The current state of the connection
*
* This method will return the current state of the porthole device
*
* * PH_STATE_NEW_SESSION = The client has connected
* * PH_STATE_CONNECTED = The client is still connected
* * PH_STATE_DISCONNECTED = There is no client connection
*/
PortholeState porthole_dev_get_state(PortholeDev handle);
/**
* Wait for the specified state
*
* @param handle The porthole device handle obtained from porthole_dev_open
* @param state The state to wait for
* @param timeout The maximum amount of time to wait in milliseconds for the state (0 = infinite)
* @return true on success, false on timeout
*/
bool porthole_dev_wait_state(PortholeDev handle, const PortholeState state, const unsigned int timeout);
/**
* 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
* @return the porthole mapping ID, or -1 on failure
*
* 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 device & driver are hard limited to 32 shares.
*/
PortholeID porthole_dev_map(PortholeDev handle, const uint32_t type, void *buffer, size_t size);
/**
* Unmap a previously shared buffer
*
* @param handle The porthole device
* @param id The porthole map id returned by porthole_dev_share
* @return true on success
*
* Unmaps 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.
*
* The client application will be notified that the mapping is about to become
* invalid and is expected to clean up and notify when it is done. If your
* application hangs calling this method the issue is very likely with your
* client application.
*/
bool porthole_dev_unmap(PortholeDev handle, PortholeID id);

View File

@@ -0,0 +1,31 @@
/*
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
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef struct
{
uint32_t id;
unsigned int size;
unsigned int num_segments;
}
PortholeMap;

View File

@@ -0,0 +1,62 @@
/*
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
*/
#pragma once
#include "types.h"
#include <sys/types.h>
/**
* Copy from memory to a PortholeMap
*
* @param src The source buffer
* @param dst The destination map
* @param len The data length to copy
* @param off The offset into the dst PortholeMap
*/
void porthole_copy_mem_to_map(void * src, PortholeMap * dst, size_t len, off_t off);
/**
* Copy from a PortholeMap to memory
*
* @param src The source buffer
* @param dst The destination buffer
* @param len The data length to copy
* @param off The offset into the src PortholeMap
*/
void porthole_copy_map_to_mem(PortholeMap * src, void * dst, size_t len, off_t off);
/**
* Copy from a PortholeMap to a PortholeMap
*
* @param src The source buffer
* @param dst The destination buffer
* @param len The data length to copy
* @param src_off The offset into the src PortholeMap
* @param dst_off The offset into the dst PortholeMap
*/
void porthole_copy_map_to_map(PortholeMap * src, PortholeMap * dst, size_t len, off_t src_off, off_t dst_off);
/**
* Get the pointer to the base of a PortholeMap
*
* @param map The map to get the pointer for
* @return The base address of the mapping, or NULL if the mapping is not contiguous
*/
void * porthole_get_map_ptr(PortholeMap *map);

View File

@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.0)
project(porthole-linux LANGUAGES C)
add_library(porthole-linux STATIC
client.c
)
target_link_libraries(porthole-linux
lg_common
)
target_include_directories(porthole-linux
PRIVATE
src
)

438
porthole/src/linux/client.c Normal file
View File

@@ -0,0 +1,438 @@
/*
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/client.h"
#include "common/objectlist.h"
#include "common/debug.h"
#include "../types.h"
#include "../phmsg.h"
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
typedef struct
{
uint32_t id;
int fd;
int refcount;
uint8_t * map;
size_t size;
}
SharedFD;
typedef struct
{
SharedFD * sfd;
uint64_t addr;
uint32_t size;
}
Segment;
typedef struct
{
uint32_t id;
ObjectList segments;
size_t size;
}
Mapping;
struct PortholeClient
{
int socket;
char rxbuf[1024];
int rxbuf_len;
PortholeMapEvent map_cb;
PortholeUnmapEvent unmap_cb;
PortholeDisconEvent discon_cb;
ObjectList fds;
ObjectList intmaps;
Mapping * current;
ObjectList maps;
bool running;
pthread_t thread;
bool thread_valid;
};
// forwards
static void * porthole_socket_thread(void * opaque);
static void porthole_free_map(Mapping * map);
static void porthole_sharedfd_free_handler(void * opaque);
static void porthole_intmaps_free_handler(void * opaque);
static void porthole_segment_free_handler(void * opaque);
static Mapping * porthole_intmap_new();
static void porthole_sharedfd_new(PortholeClient handle, const uint32_t id, const int fd);
static void porthole_segment_new(ObjectList fds, Mapping *map, const uint32_t fd_id, const uint64_t addr, const uint32_t size);
static void porthole_do_map(PortholeClient handle, Mapping * map, const uint32_t type);
// implementation
bool porthole_client_open(
PortholeClient * handle,
const char * socket_path,
PortholeMapEvent map_cb,
PortholeUnmapEvent unmap_cb,
PortholeDisconEvent discon_cb)
{
assert(handle);
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
{
DEBUG_ERROR("Failed to create a unix socket");
return false;
}
struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (connect(fd, (const struct sockaddr*)&addr, sizeof(addr)) == -1)
{
DEBUG_ERROR("Failed to connect to the socket");
close(fd);
return false;
}
*handle = (PortholeClient)calloc(sizeof(struct PortholeClient), 1);
(*handle)->socket = fd;
(*handle)->map_cb = map_cb;
(*handle)->unmap_cb = unmap_cb;
(*handle)->discon_cb = discon_cb;
(*handle)->fds = objectlist_new(porthole_sharedfd_free_handler);
(*handle)->intmaps = objectlist_new(porthole_intmaps_free_handler);
(*handle)->maps = objectlist_new(objectlist_free_item);
(*handle)->running = true;
if (pthread_create(&(*handle)->thread, NULL, porthole_socket_thread, *handle) != 0)
{
DEBUG_ERROR("Failed to create porthole socket thread");
porthole_client_close(handle);
return false;
}
(*handle)->thread_valid = true;
return true;
}
void porthole_client_close(PortholeClient * handle)
{
assert(handle && *handle);
if ((*handle)->thread_valid)
{
(*handle)->running = false;
pthread_join((*handle)->thread, NULL);
}
close((*handle)->socket);
if ((*handle)->current)
porthole_free_map((*handle)->current);
objectlist_free(&(*handle)->maps );
objectlist_free(&(*handle)->intmaps);
objectlist_free(&(*handle)->fds );
free(*handle);
*handle = NULL;
}
static void * porthole_socket_thread(void * opaque)
{
PortholeClient handle = (PortholeClient)opaque;
DEBUG_INFO("Porthole socket thread started");
while(handle->running)
{
struct iovec io =
{
.iov_base = &handle->rxbuf[handle->rxbuf_len],
.iov_len = sizeof(handle->rxbuf) - handle->rxbuf_len
};
char cbuffer[256] = {0};
struct msghdr msghdr =
{
.msg_iov = &io,
.msg_iovlen = 1,
.msg_control = &cbuffer,
.msg_controllen = sizeof(cbuffer)
};
handle->rxbuf_len = recvmsg(handle->socket, &msghdr, 0);
if (handle->rxbuf_len < 0)
{
handle->rxbuf_len = 0;
if (errno == EAGAIN || errno == EWOULDBLOCK)
continue;
DEBUG_ERROR("Failed to recieve the message");
if (handle->discon_cb)
handle->discon_cb();
break;
}
int pos = 0;
while(pos != handle->rxbuf_len)
{
PHMsg *msg = (PHMsg *)&handle->rxbuf[pos];
// check for an incomplete message
if (pos + msg->len > handle->rxbuf_len)
break;
assert(msg->len >= PH_MSG_BASE_SIZE);
pos += msg->len;
/* process the message */
switch(msg->msg)
{
case PH_MSG_MAP:
if (handle->current)
{
DEBUG_WARN("Started a new map before finishing the last one");
porthole_free_map(handle->current);
}
handle->current = porthole_intmap_new();
break;
case PH_MSG_FD:
{
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msghdr);
int * fds = (int *)CMSG_DATA(cmsg);
porthole_sharedfd_new(handle, msg->u.fd.id, fds[0]);
break;
}
case PH_MSG_SEGMENT:
{
if (!handle->current)
DEBUG_FATAL("Segment sent before map, this is a bug in the guest porthole device or driver");
porthole_segment_new(
handle->fds,
handle->current,
msg->u.segment.fd_id,
msg->u.segment.addr,
msg->u.segment.size
);
break;
}
case PH_MSG_FINISH:
if (!handle->current)
DEBUG_FATAL("Finished map before starting one");
handle->current->id = msg->u.finish.id;
objectlist_push(handle->intmaps, handle->current);
porthole_do_map(handle, handle->current, msg->u.finish.type);
handle->current = NULL;
break;
case PH_MSG_UNMAP:
{
// notify the application of the unmap
handle->unmap_cb(msg->u.unmap.id);
// remove the PortholeMap object
unsigned int count = objectlist_count(handle->maps);
for(unsigned int i = 0; i < count; ++i)
{
PortholeMap *m = (PortholeMap *)objectlist_at(handle->maps, i);
if (m->id == msg->u.unmap.id)
{
objectlist_remove(handle->maps, i);
break;
}
}
// remove the internal mapping object
count = objectlist_count(handle->intmaps);
for(unsigned int i = 0; i < count; ++i)
{
Mapping *m = (Mapping *)objectlist_at(handle->intmaps, i);
if (m->id == msg->u.unmap.id)
{
objectlist_remove(handle->intmaps, i);
break;
}
}
// reply to the guest to allow it to continue
uint32_t reply = PH_MSG_UNMAP;
msghdr.msg_controllen = 0;
io.iov_base = &reply;
io.iov_len = sizeof(reply);
if (sendmsg(handle->socket, &msghdr, 0) < 0)
{
DEBUG_ERROR("Failed to respond to the guest");
handle->running = false;
if (handle->discon_cb)
handle->discon_cb();
}
break;
}
}
}
// save any remaining bytes for the next recv
handle->rxbuf_len -= pos;
memmove(handle->rxbuf, &handle->rxbuf[pos], handle->rxbuf_len);
}
handle->running = false;
DEBUG_INFO("Porthole socket thread stopped");
return NULL;
}
static void porthole_sharedfd_new(PortholeClient handle, const uint32_t id, const int fd)
{
struct stat st;
fstat(fd, &st);
SharedFD * sfd = (SharedFD *)calloc(sizeof(SharedFD), 1);
sfd->id = id;
sfd->fd = fd;
sfd->size = st.st_size;
DEBUG_INFO("Guest FD ID %u (FD:%d, Size:%lu)", sfd->id, sfd->fd, sfd->size);
objectlist_push(handle->fds, sfd);
}
static void porthole_sharedfd_inc_ref(SharedFD * sfd)
{
if (sfd->refcount == 0)
{
sfd->map = mmap(NULL, sfd->size, PROT_READ | PROT_WRITE, MAP_SHARED, sfd->fd, 0);
if(!sfd->map)
DEBUG_FATAL("Failed to map shared memory");
}
++sfd->refcount;
}
static void porthole_sharedfd_dec_ref(SharedFD * sfd)
{
if (sfd->refcount == 0)
return;
munmap(sfd->map, sfd->size);
sfd->map = NULL;
--sfd->refcount;
}
static void porthole_sharedfd_free_handler(void * opaque)
{
SharedFD * sfd = (SharedFD *)opaque;
if (sfd->map)
{
munmap(sfd->map, sfd->size);
sfd->map = NULL;
}
close(sfd->fd);
free(sfd);
}
static Mapping * porthole_intmap_new()
{
Mapping * map = (Mapping *)calloc(sizeof(Mapping), 1);
map->segments = objectlist_new(porthole_segment_free_handler);
return map;
}
static void porthole_free_map(Mapping * map)
{
objectlist_free(&map->segments);
free(map);
}
static void porthole_intmaps_free_handler(void * opaque)
{
porthole_free_map((Mapping *)opaque);
}
static void porthole_segment_new(ObjectList fds, Mapping *map, const uint32_t fd_id, const uint64_t addr, const uint32_t size)
{
Segment * seg = calloc(sizeof(Segment), 1);
seg->addr = addr;
seg->size = size;
const unsigned int count = objectlist_count(fds);
for(unsigned int i = 0; i < count; ++i)
{
SharedFD *sfd = (SharedFD*)objectlist_at(fds, i);
if (sfd->id == fd_id)
{
seg->sfd = sfd;
break;
}
}
if (!seg->sfd)
DEBUG_FATAL("Unable to find the FD for the segment, this is a bug in the porthole device!");
map->size += size;
porthole_sharedfd_inc_ref(seg->sfd);
objectlist_push(map->segments, seg);
}
static void porthole_segment_free_handler(void * opaque)
{
Segment * seg = (Segment *)opaque;
porthole_sharedfd_dec_ref(seg->sfd);
free(seg);
}
static void porthole_do_map(PortholeClient handle, Mapping * map, const uint32_t type)
{
const unsigned int count = objectlist_count(map->segments);
PortholeMap *m = calloc(sizeof(PortholeMap) + sizeof(PortholeSegment) * count, 1);
m->id = map->id;
m->size = map->size;
m->num_segments = count;
for(unsigned int i = 0; i < count; ++i)
{
Segment * seg = (Segment *)objectlist_at(map->segments, i);
PH_SEGMENTS(m)[i].size = seg->size;
PH_SEGMENTS(m)[i].data = seg->sfd->map + seg->addr;
}
objectlist_push(handle->maps, m);
handle->map_cb(type, m);
}

64
porthole/src/phmsg.h Normal file
View File

@@ -0,0 +1,64 @@
/*
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 {
uint32_t id; // the ID of the FD
} __attribute__ ((packed)) PHMsgFd;
typedef struct {
uint32_t fd_id; // the ID of the FD for this segment
uint32_t size; // the size of this segment
uint64_t addr; // the base address of this segment
} __attribute__ ((packed)) PHMsgSegment;
typedef struct {
uint32_t type; // the application defined type
uint32_t id; // the ID of the new mapping
} __attribute__ ((packed)) PHMsgFinish;
typedef struct {
uint32_t id; // the mapping ID
} __attribute__ ((packed)) PHMsgUnmap;
typedef struct {
uint8_t len; // the message length
uint8_t msg; // the message ID
union
{
PHMsgFd fd;
PHMsgSegment segment;
PHMsgFinish finish;
PHMsgUnmap unmap;
} u;
} __attribute__ ((packed)) PHMsg;
#define PH_MSG_MAP 0x1 // start of a map sequence
#define PH_MSG_FD 0x2 // file descriptor
#define PH_MSG_SEGMENT 0x3 // map segment
#define PH_MSG_FINISH 0x4 // finish of map sequence
#define PH_MSG_UNMAP 0x5 // unmap a previous map
#define PH_MSG_BASE_SIZE (sizeof(uint8_t) + sizeof(uint8_t))
#define PH_MSG_MAP_SIZE (PH_MSG_BASE_SIZE)
#define PH_MSG_FD_SIZE (PH_MSG_BASE_SIZE + sizeof(PHMsgFd))
#define PH_MSG_SEGMENT_SIZE (PH_MSG_BASE_SIZE + sizeof(PHMsgSegment))
#define PH_MSG_FINISH_SIZE (PH_MSG_BASE_SIZE + sizeof(PHMsgFinish))
#define PH_MSG_UNMAP_SIZE (PH_MSG_BASE_SIZE + sizeof(PHMsgUnmap))

18
porthole/src/porthole.c Normal file
View 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
*/

31
porthole/src/types.h Normal file
View File

@@ -0,0 +1,31 @@
/*
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
*/
#pragma once
#include "porthole/types.h"
typedef struct
{
unsigned int size;
void * data;
}
PortholeSegment;
#define PH_SEGMENTS(map) ((PortholeSegment *)((map)+1))

161
porthole/src/util.c Normal file
View File

@@ -0,0 +1,161 @@
/*
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 "types.h"
#include "porthole/util.h"
#include "common/debug.h"
#include <string.h>
void porthole_copy_mem_to_map(void * src, PortholeMap * dst, size_t len, off_t off)
{
if (off + len > dst->size)
DEBUG_FATAL("Attempt to write beyond the length of destination mapping");
/* find the start segment */
PortholeSegment * seg = PH_SEGMENTS(dst);
while(off)
{
if (seg->size > off)
break;
off -= seg->size;
++seg;
}
/* copy into each segment until the length has been satisfied */
while(len)
{
char * buf = (char *)seg->data + off;
size_t avail = seg->size - off;
off = 0;
if (avail > len)
avail = len;
memcpy(buf, src, avail);
src = (char *)src + avail;
len -= avail;
++seg;
}
}
void porthole_copy_map_to_mem(PortholeMap * src, void * dst, size_t len, off_t off)
{
if (off + len > src->size)
DEBUG_FATAL("Attempt to read beyond the length of the source mapping");
/* find the start segment */
PortholeSegment * seg = PH_SEGMENTS(src);
while(off)
{
if (seg->size > off)
break;
off -= seg->size;
++seg;
}
/* copy from each segment until the length has been satisfied */
while(len)
{
char * buf = (char *)seg->data + off;
size_t avail = seg->size - off;
off = 0;
if (avail > len)
avail = len;
memcpy(dst, buf, avail);
dst = (char *)dst + avail;
len -= avail;
++seg;
}
}
void porthole_copy_map_to_map(PortholeMap * src, PortholeMap * dst, size_t len, off_t src_off, off_t dst_off)
{
if (src_off + len > src->size)
DEBUG_FATAL("Attempt to read beyond th elength of the source mapping");
if (dst_off + len > dst->size)
DEBUG_FATAL("Attempt to write beyond the length of the destination mapping");
/* find the start segments */
PortholeSegment * src_seg = PH_SEGMENTS(src);
while(src_off)
{
if (src_seg->size > src_off)
break;
src_off -= src_seg->size;
++src_seg;
}
PortholeSegment * dst_seg = PH_SEGMENTS(dst);
while(dst_off)
{
if (dst_seg->size > dst_off)
break;
dst_off -= dst_seg->size;
++dst_seg;
}
while(len)
{
char * src_buf = (char *)src_seg->data + src_off;
char * dst_buf = (char *)dst_seg->data + dst_off;
size_t src_avail = src_seg->size - src_off;
size_t dst_avail = dst_seg->size - dst_off;
src_off = 0;
dst_off = 0;
size_t avail = src_avail > dst_avail ? dst_avail : src_avail;
if (avail > len)
avail = len;
memcpy(dst_buf, src_buf, avail);
src_avail -= avail;
dst_avail -= avail;
if (src_avail == 0)
++src_seg;
else
src_off = src_avail;
if (dst_avail == 0)
++dst_seg;
else
dst_off = dst_avail;
len -= avail;
}
}
void * porthole_get_map_ptr(PortholeMap *map)
{
if (map->num_segments != 1)
return NULL;
return PH_SEGMENTS(map)[0].data;
}

View 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
)

View File

@@ -0,0 +1,256 @@
/*
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>
#include <assert.h>
struct PortholeDev
{
HANDLE dev;
bool connected;
PortholeEvents events;
};
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;
assert(handle);
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;
/* create the events and register them */
(*handle)->events.connect = CreateEvent(NULL, FALSE, FALSE, NULL);
(*handle)->events.disconnect = CreateEvent(NULL, FALSE, FALSE, NULL);
DWORD returned;
if (!DeviceIoControl(dev, IOCTL_PORTHOLE_REGISTER_EVENTS, &(*handle)->events, sizeof(PortholeEvents), NULL, 0, &returned, NULL))
{
DEBUG_ERROR("Failed to register the events");
CloseHandle((*handle)->events.connect );
CloseHandle((*handle)->events.disconnect);
CloseHandle(dev);
free(*handle);
*handle = NULL;
return false;
}
return true;
}
void porthole_dev_close(PortholeDev *handle)
{
assert(handle && *handle);
CloseHandle((*handle)->events.connect );
CloseHandle((*handle)->events.disconnect);
CloseHandle((*handle)->dev);
free(*handle);
*handle = NULL;
}
static PortholeState get_state(PortholeDev handle, unsigned int timeout)
{
if (handle->connected)
{
switch(WaitForSingleObject(handle->events.disconnect, timeout))
{
case WAIT_OBJECT_0:
handle->connected = false;
return PH_STATE_DISCONNECTED;
case WAIT_TIMEOUT:
return PH_STATE_CONNECTED;
default:
DEBUG_FATAL("Error waiting on disconnect event");
break;
}
}
switch(WaitForSingleObject(handle->events.connect, timeout))
{
case WAIT_OBJECT_0:
handle->connected = true;
return PH_STATE_NEW_SESSION;
case WAIT_TIMEOUT:
return PH_STATE_DISCONNECTED;
default:
DEBUG_FATAL("Error waiting on connection event");
break;
}
}
PortholeState porthole_dev_get_state(PortholeDev handle)
{
return get_state(handle, 0);
}
bool porthole_dev_wait_state(PortholeDev handle, const PortholeState state, const unsigned int timeout)
{
const DWORD to = (timeout == 0) ? INFINITE : timeout;
PortholeState lastState = get_state(handle, 0);
if (state == lastState)
return true;
while(true)
{
PortholeState nextState;
switch(lastState)
{
case PH_STATE_DISCONNECTED:
nextState = PH_STATE_NEW_SESSION;
break;
case PH_STATE_NEW_SESSION:
nextState = PH_STATE_CONNECTED;
break;
case PH_STATE_CONNECTED:
nextState = PH_STATE_DISCONNECTED;
break;
}
PortholeState newState = get_state(handle, to);
if (newState == lastState || newState != nextState)
return false;
if (newState == state)
return true;
lastState = newState;
}
}
PortholeID porthole_dev_map(PortholeDev handle, const uint32_t type, void *buffer, size_t size)
{
assert(handle);
DWORD returned;
PortholeMsg msg = {
.type = type,
.addr = buffer,
.size = size
};
PortholeMapID out;
if (!DeviceIoControl(handle->dev, IOCTL_PORTHOLE_SEND_MSG, &msg, sizeof(PortholeMsg), &out, sizeof(PortholeMapID), &returned, NULL))
return -1;
PortholeID ret = out;
return ret;
}
bool porthole_dev_unmap(PortholeDev handle, PortholeID id)
{
assert(handle);
DWORD returned;
PortholeMapID msg = id;
if (!DeviceIoControl(handle->dev, IOCTL_PORTHOLE_UNLOCK_BUFFER, &msg, sizeof(PortholeMapID), NULL, 0, &returned, NULL))
return false;
return true;
}

View File

@@ -0,0 +1,27 @@
#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 int PortholeMapID, *PPortholeMapID;
typedef struct _PortholeEvents
{
HANDLE connect;
HANDLE disconnect;
}
PortholeEvents, *PPortholeEvents;
#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)
#define IOCTL_PORTHOLE_REGISTER_EVENTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)