ivshmem work
This commit is contained in:
parent
11766f6385
commit
7a6ef52144
20 changed files with 724 additions and 162 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,3 +5,6 @@
|
||||||
/agent/bin
|
/agent/bin
|
||||||
/agent/obj
|
/agent/obj
|
||||||
/agent/compile_commands.json
|
/agent/compile_commands.json
|
||||||
|
|
||||||
|
# test stuff
|
||||||
|
/shared/src/test
|
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -47,6 +47,12 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dlib"
|
name = "dlib"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -83,7 +89,9 @@ name = "fbcserver"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"libc",
|
||||||
"minifb",
|
"minifb",
|
||||||
|
"nix 0.29.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -204,9 +212,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.164"
|
version = "0.2.167"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
@ -294,6 +302,18 @@ dependencies = [
|
||||||
"memoffset",
|
"memoffset",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.20.2"
|
version = "1.20.2"
|
||||||
|
@ -558,7 +578,7 @@ dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"downcast-rs",
|
"downcast-rs",
|
||||||
"libc",
|
"libc",
|
||||||
"nix",
|
"nix 0.24.3",
|
||||||
"scoped-tls",
|
"scoped-tls",
|
||||||
"wayland-commons",
|
"wayland-commons",
|
||||||
"wayland-scanner",
|
"wayland-scanner",
|
||||||
|
@ -571,7 +591,7 @@ version = "0.29.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
|
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nix",
|
"nix 0.24.3",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"wayland-sys",
|
"wayland-sys",
|
||||||
|
@ -583,7 +603,7 @@ version = "0.29.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
|
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nix",
|
"nix 0.24.3",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"xcursor",
|
"xcursor",
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,4 +5,6 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
|
libc = "0.2.167"
|
||||||
minifb = "0.27.0"
|
minifb = "0.27.0"
|
||||||
|
nix = { version = "0.29.0", features = [ "stat", "mman" ] }
|
||||||
|
|
|
@ -9,6 +9,7 @@ OBJDIR = obj/$(ARCH)/$(CONFIG)
|
||||||
VPATH = src/crt \
|
VPATH = src/crt \
|
||||||
src/base \
|
src/base \
|
||||||
src/ \
|
src/ \
|
||||||
|
../shared/src
|
||||||
|
|
||||||
# Objects for crt
|
# Objects for crt
|
||||||
OBJS_STARTUP := $(OBJDIR)/crt0.o \
|
OBJS_STARTUP := $(OBJDIR)/crt0.o \
|
||||||
|
@ -21,6 +22,7 @@ OBJS_BASELIB :=
|
||||||
OBJS := $(OBJDIR)/capture.o \
|
OBJS := $(OBJDIR)/capture.o \
|
||||||
$(OBJDIR)/capture_nvfbc.o \
|
$(OBJDIR)/capture_nvfbc.o \
|
||||||
$(OBJDIR)/nvfbc_library.o \
|
$(OBJDIR)/nvfbc_library.o \
|
||||||
|
$(OBJDIR)/ivshmem.o \
|
||||||
$(OBJDIR)/main.o \
|
$(OBJDIR)/main.o \
|
||||||
|
|
||||||
LINK_LIBS := $(VS2022_PATH)/ucrt/lib/$(ARCH)/libucrt$(D).lib \
|
LINK_LIBS := $(VS2022_PATH)/ucrt/lib/$(ARCH)/libucrt$(D).lib \
|
||||||
|
@ -30,6 +32,7 @@ LINK_LIBS := $(VS2022_PATH)/ucrt/lib/$(ARCH)/libucrt$(D).lib \
|
||||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/kernel32.lib \
|
$(VS2022_PATH)/winsdk/lib/$(ARCH)/kernel32.lib \
|
||||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/user32.lib \
|
$(VS2022_PATH)/winsdk/lib/$(ARCH)/user32.lib \
|
||||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/comctl32.lib \
|
$(VS2022_PATH)/winsdk/lib/$(ARCH)/comctl32.lib \
|
||||||
|
$(VS2022_PATH)/winsdk/lib/$(ARCH)/setupapi.lib \
|
||||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/ws2_32.lib
|
$(VS2022_PATH)/winsdk/lib/$(ARCH)/ws2_32.lib
|
||||||
|
|
||||||
.PHONY: all dumpinfo clean matrix
|
.PHONY: all dumpinfo clean matrix
|
||||||
|
@ -40,6 +43,11 @@ publish: $(BINDIR)/$(NAME).exe
|
||||||
cp -rv bin /data/sda/shit/Release/clangent
|
cp -rv bin /data/sda/shit/Release/clangent
|
||||||
chmod -Rvv 777 /data/sda/shit/Release/clangent
|
chmod -Rvv 777 /data/sda/shit/Release/clangent
|
||||||
|
|
||||||
|
|
||||||
|
publish2: $(BINDIR)/$(NAME).exe
|
||||||
|
cp -rv bin/ /data/sda/shit/Release/ivshtest/
|
||||||
|
chmod -Rvv 777 /data/sda/shit/Release/ivshtest
|
||||||
|
|
||||||
dumpinfo:
|
dumpinfo:
|
||||||
echo -e "\e[96mBuilding configuration $(CONFIG) for arch $(ARCH) \e[0m"
|
echo -e "\e[96mBuilding configuration $(CONFIG) for arch $(ARCH) \e[0m"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ SDK_INCLUDES := -isystem $(VS2022_PATH)/ucrt/include -isystem $(VS2022_PATH)/crt
|
||||||
|
|
||||||
# Windows 6.1
|
# Windows 6.1
|
||||||
BASE_CCFLAGS := $(SDK_INCLUDES) -D_WIN32_WINNT=0x0601 -Ires -std=c17
|
BASE_CCFLAGS := $(SDK_INCLUDES) -D_WIN32_WINNT=0x0601 -Ires -std=c17
|
||||||
BASE_CXXFLAGS := $(SDK_INCLUDES) -D_CRT_SECURE_NO_WARNINGS -fno-exceptions -fno-rtti -D_WIN32_WINNT=0x0601 -Isrc -Ires -std=c++23
|
BASE_CXXFLAGS := $(SDK_INCLUDES) -D_CRT_SECURE_NO_WARNINGS -fno-exceptions -fno-rtti -D_WIN32_WINNT=0x0601 -I../shared/src -Isrc -Ires -std=c++23
|
||||||
|
|
||||||
Release_Valid = yes
|
Release_Valid = yes
|
||||||
Release_CCFLAGS = -O3 -ffast-math -DNDEBUG
|
Release_CCFLAGS = -O3 -ffast-math -DNDEBUG
|
||||||
|
|
69
agent/sdk/inc/virtio/ivshmem.h
Normal file
69
agent/sdk/inc/virtio/ivshmem.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#include <initguid.h>
|
||||||
|
|
||||||
|
DEFINE_GUID (GUID_DEVINTERFACE_IVSHMEM,
|
||||||
|
0xdf576976,0x569d,0x4672,0x95,0xa0,0xf5,0x7e,0x4e,0xa0,0xb2,0x10);
|
||||||
|
// {df576976-569d-4672-95a0-f57e4ea0b210}
|
||||||
|
|
||||||
|
typedef UINT16 IVSHMEM_PEERID;
|
||||||
|
typedef UINT64 IVSHMEM_SIZE;
|
||||||
|
|
||||||
|
#define IVSHMEM_CACHE_NONCACHED 0
|
||||||
|
#define IVSHMEM_CACHE_CACHED 1
|
||||||
|
#define IVSHMEM_CACHE_WRITECOMBINED 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
This structure is for use with the IOCTL_IVSHMEM_REQUEST_MMAP IOCTL
|
||||||
|
*/
|
||||||
|
typedef struct IVSHMEM_MMAP_CONFIG
|
||||||
|
{
|
||||||
|
UINT8 cacheMode; // the caching mode of the mapping, see IVSHMEM_CACHE_* for options
|
||||||
|
}
|
||||||
|
IVSHMEM_MMAP_CONFIG, *PIVSHMEM_MMAP_CONFIG;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This structure is for use with the IOCTL_IVSHMEM_REQUEST_MMAP IOCTL
|
||||||
|
*/
|
||||||
|
typedef struct IVSHMEM_MMAP
|
||||||
|
{
|
||||||
|
IVSHMEM_PEERID peerID; // our peer id
|
||||||
|
IVSHMEM_SIZE size; // the size of the memory region
|
||||||
|
PVOID ptr; // pointer to the memory region
|
||||||
|
UINT16 vectors; // the number of vectors available
|
||||||
|
}
|
||||||
|
IVSHMEM_MMAP, *PIVSHMEM_MMAP;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This structure is for use with the IOCTL_IVSHMEM_RING_DOORBELL IOCTL
|
||||||
|
*/
|
||||||
|
typedef struct IVSHMEM_RING
|
||||||
|
{
|
||||||
|
IVSHMEM_PEERID peerID; // the id of the peer to ring
|
||||||
|
UINT16 vector; // the doorbell to ring
|
||||||
|
}
|
||||||
|
IVSHMEM_RING, *PIVSHMEM_RING;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This structure is for use with the IOCTL_IVSHMEM_REGISTER_EVENT IOCTL
|
||||||
|
|
||||||
|
Please Note:
|
||||||
|
- The IVSHMEM driver has a hard limit of 32 events.
|
||||||
|
- Events that are singleShot are released after they have been set.
|
||||||
|
- At this time repeating events are only released when the driver device
|
||||||
|
handle is closed, closing the event handle doesn't release it from the
|
||||||
|
drivers list. While this won't cause a problem in the driver, it will
|
||||||
|
cause you to run out of event slots.
|
||||||
|
*/
|
||||||
|
typedef struct IVSHMEM_EVENT
|
||||||
|
{
|
||||||
|
UINT16 vector; // the vector that triggers the event
|
||||||
|
HANDLE event; // the event to trigger
|
||||||
|
BOOLEAN singleShot; // set to TRUE if you want the driver to only trigger this event once
|
||||||
|
}
|
||||||
|
IVSHMEM_EVENT, *PIVSHMEM_EVENT;
|
||||||
|
|
||||||
|
#define IOCTL_IVSHMEM_REQUEST_PEERID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_IVSHMEM_REQUEST_SIZE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_IVSHMEM_REQUEST_MMAP CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_IVSHMEM_RELEASE_MMAP CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_IVSHMEM_RING_DOORBELL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_IVSHMEM_REGISTER_EVENT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
@ -1,4 +1,5 @@
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
#include "ivshmem.hpp"
|
||||||
#pragma comment(lib, "ws2_32.lib")
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
#include <WinSock2.h>
|
#include <WinSock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
|
@ -11,170 +12,69 @@
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
enum class MessageType : u32 {
|
|
||||||
Resize, // tResizeMessage
|
|
||||||
Data, // tDataMessage
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tMessageHeader {
|
#include "atomic_spinlock.hpp"
|
||||||
MessageType type;
|
|
||||||
u32 datalen;
|
|
||||||
// data
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tResizeMessage {
|
|
||||||
u32 width;
|
|
||||||
u32 height;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tTile {
|
|
||||||
u32 tile_x;
|
|
||||||
u32 tile_y;
|
|
||||||
|
|
||||||
u32 tile_width;
|
|
||||||
u32 tile_height;
|
|
||||||
|
|
||||||
// u32 tile_rgba[tile_width*tile_height]
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tDataMessage {
|
|
||||||
u32 tileCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
struct tileRect {
|
struct tileRect {
|
||||||
u32 x, y, width, height;
|
u32 x, y, width, height;
|
||||||
};
|
};
|
||||||
|
|
||||||
// client for streamserver
|
struct Test {
|
||||||
class cStreamClient {
|
hazelnut::AtomicSpinlock lk{};
|
||||||
SOCKET tcpSocket;
|
std::atomic<u32> sessionId {};
|
||||||
|
std::atomic<u32> pingPong{};
|
||||||
public:
|
|
||||||
cStreamClient() = default;
|
|
||||||
|
|
||||||
~cStreamClient() {
|
|
||||||
if(tcpSocket != -1) {
|
|
||||||
closesocket(tcpSocket);
|
|
||||||
tcpSocket = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Connect(const char* address, int port) {
|
|
||||||
tcpSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
if(tcpSocket == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sockaddr_in clientSvc {};
|
|
||||||
clientSvc.sin_family = AF_INET;
|
|
||||||
inet_pton(AF_INET, address, &clientSvc.sin_addr.s_addr);
|
|
||||||
clientSvc.sin_port = htons(port);
|
|
||||||
|
|
||||||
if(connect(tcpSocket, (SOCKADDR*)&clientSvc, sizeof(clientSvc)) == SOCKET_ERROR) {
|
|
||||||
printf("No connection socket. Fuck you\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable Nagle's alrogithm
|
|
||||||
int nodelay = 1;
|
|
||||||
setsockopt(tcpSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nodelay, sizeof(nodelay));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendResize(const tResizeMessage& resize) {
|
|
||||||
tMessageHeader header;
|
|
||||||
header.type = MessageType::Resize;
|
|
||||||
header.datalen = sizeof(tResizeMessage);
|
|
||||||
|
|
||||||
send(tcpSocket, (const char*)&header, sizeof(header), 0);
|
|
||||||
send(tcpSocket, (const char*)&resize, sizeof(resize), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendData(const u32* pData, u32 width, u32 height, const std::vector<tileRect>& tiles, bool sendFull) {
|
|
||||||
tMessageHeader header;
|
|
||||||
header.type = MessageType::Data;
|
|
||||||
// header.datalen = data.get_size() * sizeof(UINT32);
|
|
||||||
header.datalen = sizeof(tDataMessage);
|
|
||||||
|
|
||||||
// send tile header
|
|
||||||
// bool sendFull = false;
|
|
||||||
tDataMessage dm;
|
|
||||||
|
|
||||||
// if(tiles.empty()) {
|
|
||||||
// sendFull = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if(sendFull) {
|
|
||||||
dm.tileCount = 1;
|
|
||||||
} else {
|
|
||||||
dm.tileCount = (u32)tiles.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if(tiles.empty()) {
|
|
||||||
// Send the full screen as a "tile"
|
|
||||||
dm.tileCount = 1;
|
|
||||||
sendFull = true;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// we have writev() at home
|
|
||||||
// writev() at home:
|
|
||||||
send(tcpSocket, (const char*)&header, sizeof(header), 0);
|
|
||||||
send(tcpSocket, (const char*)&dm, sizeof(dm), 0);
|
|
||||||
|
|
||||||
// send each tile
|
|
||||||
if(!sendFull) {
|
|
||||||
for(auto& tile : tiles) {
|
|
||||||
tTile tile_wire { tile.x, tile.y, tile.width, tile.height };
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
std::vector<u32> tileData;
|
|
||||||
tileData.resize(tile.width * tile.height);
|
|
||||||
|
|
||||||
for(u32 y = 0; y < tile.height; ++y) {
|
|
||||||
auto* pTileLineStart = &pData[(tile.y + y) * width + tile.x];
|
|
||||||
memcpy(&tileData[y * tile.width], pTileLineStart, tile.width * sizeof(u32));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// send header
|
|
||||||
send(tcpSocket, (const char*)&tile_wire, (i32)sizeof(tile_wire), 0);
|
|
||||||
// send(tcpSocket, (const char*)&tileData[0], (i32)tileData.size() * 4, 0);
|
|
||||||
|
|
||||||
// send data now
|
|
||||||
/*for (auto y = tile.y; y < tile.y + tile.height; ++y) {
|
|
||||||
auto* pTileLineStart = &pData[y * width + tile.x];
|
|
||||||
send(tcpSocket, (const char*)pTileLineStart, tile.width * sizeof(UINT32), 0);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tTile tDummyTile { 0, 0, width, height };
|
|
||||||
send(tcpSocket, (const char*)&tDummyTile, sizeof(tDummyTile), 0);
|
|
||||||
// send(tcpSocket, (const char*)&pData[0], (i32)((width * height) * sizeof(u32)), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
send(tcpSocket, (const char*)&pData[0], (i32)((width * height) * sizeof(u32)), 0);
|
|
||||||
// send(tcpSocket, (const char*)&data.data()[0], data.get_size() * sizeof(UINT32), 0);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
WSADATA data;
|
hazelnut::IvshmemDevice dev;
|
||||||
if(WSAStartup(MAKEWORD(2, 2), &data) != NO_ERROR) {
|
|
||||||
|
if(!dev.Open()) {
|
||||||
|
printf("Failed to open ivshmem device\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cStreamClient client;
|
auto size = dev.GetSize();
|
||||||
|
auto ptr = (u32*)dev.GetPointer();
|
||||||
|
|
||||||
if(!client.Connect("192.168.1.149", 9438)) {
|
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
|
||||||
printf("conn failed\n");
|
|
||||||
return 1;
|
// wipe the first 1mb
|
||||||
|
memset(&ptr[0], 0, 1*(1024*1024));
|
||||||
|
|
||||||
|
printf("wiped memory\n");
|
||||||
|
|
||||||
|
// Setup a test struct at the start of ivshmem
|
||||||
|
auto* pTest = new(&ptr[0]) Test;
|
||||||
|
|
||||||
|
// reset pingpong counter
|
||||||
|
pTest->pingPong.store(0);
|
||||||
|
|
||||||
|
u32 tries = 0;
|
||||||
|
u32 curTries = 10;
|
||||||
|
|
||||||
|
pTest->sessionId.store(rand());
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
// lock
|
||||||
|
{
|
||||||
|
auto guard = pTest->lk.lock();
|
||||||
|
Sleep(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
printf("pingpong %u\n", pTest->pingPong.load());
|
||||||
|
|
||||||
|
if(tries++ == curTries) {
|
||||||
|
tries = 0;
|
||||||
|
pTest->pingPong.fetch_add(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if 0
|
||||||
printf("Hazelnut agent\n");
|
printf("Hazelnut agent\n");
|
||||||
|
|
||||||
// Create a capture interface
|
// Create a capture interface
|
||||||
|
@ -216,8 +116,7 @@ int main(int argc, char** argv) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send that to the server
|
|
||||||
client.SendData(framebuffer.pFramebuffer, framebuffer.width, framebuffer.height, tiles, firstFrame == false);
|
|
||||||
|
|
||||||
if(firstFrame)
|
if(firstFrame)
|
||||||
firstFrame = false;
|
firstFrame = false;
|
||||||
|
@ -227,16 +126,12 @@ int main(int argc, char** argv) {
|
||||||
diff = capture->GetDiffInformation();
|
diff = capture->GetDiffInformation();
|
||||||
firstFrame = true;
|
firstFrame = true;
|
||||||
|
|
||||||
client.SendResize({ framebuffer.width, framebuffer.height });
|
|
||||||
|
|
||||||
// send empty frame
|
|
||||||
tiles.clear();
|
|
||||||
client.SendData(framebuffer.pFramebuffer, framebuffer.width, framebuffer.height, tiles, true);
|
|
||||||
} else {
|
} else {
|
||||||
printf("Failed to capture\n");
|
printf("Failed to capture\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
3
shared/README.md
Normal file
3
shared/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# shared code
|
||||||
|
|
||||||
|
This is shared between the two ends. It is cross platform C++20.
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
// common types
|
// common types
|
||||||
using u8 = std::uint8_t;
|
using u8 = std::uint8_t;
|
49
shared/src/atomic_spinlock.hpp
Normal file
49
shared/src/atomic_spinlock.hpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdio> //debugging
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
namespace hazelnut {
|
||||||
|
|
||||||
|
/// A atomic spinlock. Can be held across threads, or virtual machines.
|
||||||
|
struct AtomicSpinlock {
|
||||||
|
struct LockGuard {
|
||||||
|
~LockGuard() { lock.unlock(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend AtomicSpinlock;
|
||||||
|
LockGuard(AtomicSpinlock& lock) : lock(lock) {}
|
||||||
|
AtomicSpinlock& lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
AtomicSpinlock() { __lock.store(0, std::memory_order::seq_cst); }
|
||||||
|
|
||||||
|
LockGuard lock() {
|
||||||
|
lock_manually();
|
||||||
|
return LockGuard(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock_manually() {
|
||||||
|
u32 expected = 0;
|
||||||
|
while(!__lock.compare_exchange_strong(expected, 1, std::memory_order::seq_cst)) {
|
||||||
|
//printf("LOCK CONTENDED\n");
|
||||||
|
expected = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_lock_manually() {
|
||||||
|
u32 expected = 0;
|
||||||
|
return __lock.compare_exchange_strong(expected, 1, std::memory_order::seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() { __lock.store(0, std::memory_order::seq_cst); }
|
||||||
|
|
||||||
|
std::atomic_uint32_t __lock;
|
||||||
|
};
|
||||||
|
} // namespace hazelnut
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
3
shared/src/build_test.sh
Executable file
3
shared/src/build_test.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -xeuo pipefail
|
||||||
|
g++ -O3 -std=c++20 ivshmem.cpp test.cpp -o test
|
40
shared/src/ivshmem.cpp
Normal file
40
shared/src/ivshmem.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#include "ivshmem.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "ivshmem_windows.cpp"
|
||||||
|
#elif __linux__
|
||||||
|
#include "ivshmem_linux.cpp"
|
||||||
|
#else
|
||||||
|
#error YOU fucked
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace hazelnut {
|
||||||
|
|
||||||
|
IvshmemDevice::IvshmemDevice() {
|
||||||
|
pImpl = new Impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
IvshmemDevice::~IvshmemDevice() {
|
||||||
|
Close();
|
||||||
|
delete pImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IvshmemDevice::Open(const char* devName) {
|
||||||
|
return pImpl->Open(devName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IvshmemDevice::Close() {
|
||||||
|
pImpl->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* IvshmemDevice::GetPointer() {
|
||||||
|
return pImpl->GetPointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
usize IvshmemDevice::GetSize() {
|
||||||
|
return pImpl->GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace hazelnut
|
31
shared/src/ivshmem.hpp
Normal file
31
shared/src/ivshmem.hpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
namespace hazelnut {
|
||||||
|
|
||||||
|
/// Cross-platform IVSHMEM.
|
||||||
|
struct IvshmemDevice {
|
||||||
|
IvshmemDevice();
|
||||||
|
|
||||||
|
// forbid copy/move
|
||||||
|
IvshmemDevice(const IvshmemDevice&) = delete;
|
||||||
|
IvshmemDevice(IvshmemDevice&&) = delete;
|
||||||
|
|
||||||
|
~IvshmemDevice();
|
||||||
|
|
||||||
|
/// Opens the device. On Linux [devName] MUST point to a SHMEM file.
|
||||||
|
bool Open(const char* devName = nullptr);
|
||||||
|
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
/// Gets pointer to IVSHMEM memory.
|
||||||
|
void* GetPointer();
|
||||||
|
|
||||||
|
usize GetSize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Impl;
|
||||||
|
Impl* pImpl {};
|
||||||
|
};
|
||||||
|
} // namespace hazelnut
|
82
shared/src/ivshmem_linux.cpp
Normal file
82
shared/src/ivshmem_linux.cpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
namespace hazelnut {
|
||||||
|
|
||||||
|
struct IvshmemDevice::Impl {
|
||||||
|
int fd;
|
||||||
|
void* pMap = nullptr;
|
||||||
|
usize size = 0;
|
||||||
|
|
||||||
|
bool OpenDevice(const char* devName) {
|
||||||
|
// devName is not optional on Linux.
|
||||||
|
if(devName == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int devFd = open(devName, O_RDWR, static_cast<mode_t>(0600));
|
||||||
|
|
||||||
|
if(devFd == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fd = devFd;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MapMemory() {
|
||||||
|
// Device was opened, let's map memory
|
||||||
|
|
||||||
|
struct stat st {};
|
||||||
|
if(fstat(fd, &st) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
usize devSize = st.st_size;
|
||||||
|
|
||||||
|
void* map = mmap(0, devSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if(map == MAP_FAILED) {
|
||||||
|
printf("map failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pMap = map;
|
||||||
|
size = devSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnmapMemory() {
|
||||||
|
// Unmap memory.
|
||||||
|
if(pMap)
|
||||||
|
munmap(pMap, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Open(const char* pDevName) {
|
||||||
|
if(!OpenDevice(pDevName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!MapMemory()) {
|
||||||
|
Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() {
|
||||||
|
UnmapMemory();
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GetPointer() { return pMap; }
|
||||||
|
|
||||||
|
usize GetSize() { return size; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hazelnut
|
40
shared/src/ivshmem_protocol.hpp
Normal file
40
shared/src/ivshmem_protocol.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
#include <atomic>
|
||||||
|
#include "atomic_spinlock.hpp"
|
||||||
|
|
||||||
|
namespace hazelnut {
|
||||||
|
|
||||||
|
#pragma pack(push, 4)
|
||||||
|
|
||||||
|
|
||||||
|
/// Header for Hazelnut ivshmem. At 0x0 in the ivshmem memory space.
|
||||||
|
struct alignas(4096) IvshHeader {
|
||||||
|
std::atomic<u32> magic;
|
||||||
|
|
||||||
|
// When this lock is held by the host, the host can read the rest of memory freely.
|
||||||
|
// We wait for the host to release before updating memory ourselves.
|
||||||
|
AtomicSpinlock lock;
|
||||||
|
|
||||||
|
// See the next page boundary for FrameHeader
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Stored at a page boundary
|
||||||
|
struct alignas(4096) FrameHeader {
|
||||||
|
/// The serial of the frame. If this is unchanged between a
|
||||||
|
/// lock-grab-unlock cycle, the frame has not changed.
|
||||||
|
std::atomic<u32> serial;
|
||||||
|
|
||||||
|
// size
|
||||||
|
std::atomic<u32> width;
|
||||||
|
std::atomic<u32> height;
|
||||||
|
|
||||||
|
// obtain bits at the next page!
|
||||||
|
u32* bits() {
|
||||||
|
return reinterpret_cast<u32*>(this + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
}
|
168
shared/src/ivshmem_windows.cpp
Normal file
168
shared/src/ivshmem_windows.cpp
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#include <windows.h>
|
||||||
|
#include <setupapi.h>
|
||||||
|
|
||||||
|
// IVSHMEM ioctls
|
||||||
|
#include <virtio/ivshmem.h>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
|
namespace hazelnut {
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct free_deleter {
|
||||||
|
void operator()(T* ptr) {
|
||||||
|
if(ptr)
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// DOES NOT UNBLESS WITH ->~T() !!!
|
||||||
|
template <class T>
|
||||||
|
using unique_malloc_ptr = std::unique_ptr<T, free_deleter<T>>;
|
||||||
|
|
||||||
|
// does not bless with placement new
|
||||||
|
template <class T>
|
||||||
|
unique_malloc_ptr<T> unique_malloc(usize len) {
|
||||||
|
return { reinterpret_cast<T*>(malloc(len)), free_deleter<T> {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
struct scoped_devinfo {
|
||||||
|
scoped_devinfo(HDEVINFO hDevInfo) : hDevInfo(hDevInfo) {}
|
||||||
|
|
||||||
|
~scoped_devinfo() { SetupDiDestroyDeviceInfoList(hDevInfo); }
|
||||||
|
|
||||||
|
scoped_devinfo(const scoped_devinfo&) = delete;
|
||||||
|
scoped_devinfo(scoped_devinfo&&) = delete;
|
||||||
|
|
||||||
|
operator HDEVINFO() { return hDevInfo; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
HDEVINFO hDevInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IvshmemDevice::Impl {
|
||||||
|
HANDLE hIvshmem { INVALID_HANDLE_VALUE };
|
||||||
|
IVSHMEM_MMAP ivshmemMmapStruct {};
|
||||||
|
|
||||||
|
bool OpenDevice() {
|
||||||
|
memset(&ivshmemMmapStruct, 0, sizeof(IVSHMEM_MMAP));
|
||||||
|
|
||||||
|
SP_DEVINFO_DATA devInfoData = {
|
||||||
|
.cbSize = sizeof(SP_DEVINFO_DATA),
|
||||||
|
};
|
||||||
|
|
||||||
|
SP_DEVICE_INTERFACE_DATA devInterfaceData { .cbSize = sizeof(SP_DEVICE_INTERFACE_DATA) };
|
||||||
|
|
||||||
|
auto hDevInfo =
|
||||||
|
scoped_devinfo { SetupDiGetClassDevsA(&GUID_DEVINTERFACE_IVSHMEM, nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE) };
|
||||||
|
|
||||||
|
// just do the first one for now
|
||||||
|
SetupDiEnumDeviceInfo(hDevInfo, 0, &devInfoData);
|
||||||
|
|
||||||
|
if(auto le = GetLastError(); le != ERROR_SUCCESS && le != ERROR_NO_MORE_ITEMS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SetupDiEnumDeviceInterfaces(hDevInfo, &devInfoData, &GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD dwReq = 0;
|
||||||
|
SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterfaceData, nullptr, 0, &dwReq, nullptr);
|
||||||
|
|
||||||
|
if(!dwReq) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pData = unique_malloc<SP_DEVICE_INTERFACE_DETAIL_DATA>(dwReq);
|
||||||
|
pData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||||
|
|
||||||
|
if(!SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterfaceData, pData.get(), dwReq, nullptr, nullptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hIvshmem = CreateFile(pData->DevicePath, 0, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||||
|
|
||||||
|
if(hIvshmem == INVALID_HANDLE_VALUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TInput, class TOutput>
|
||||||
|
bool Ioctl(u32 ctlCode, TInput& in, TOutput& out) {
|
||||||
|
// Needed because some versions of the IVSHMEM driver, notably the last one
|
||||||
|
// that works on Windows 7, assumes return length is not nullptr, and will crash
|
||||||
|
// the calling process if it is nullptr. Red Hat quality code everyone; it doesn't
|
||||||
|
// just envelope the Linux ecosystem - it even gets to take over the Windows one too!
|
||||||
|
DWORD dwDummyRetLength = 0;
|
||||||
|
|
||||||
|
if(!DeviceIoControl(hIvshmem, ctlCode, reinterpret_cast<void*>(&in), sizeof(TInput), reinterpret_cast<void*>(&out), sizeof(TOutput),
|
||||||
|
&dwDummyRetLength, nullptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ioctl() overload for bare
|
||||||
|
bool Ioctl(u32 ctlCode) {
|
||||||
|
DWORD dwDummyRetLength = 0;
|
||||||
|
if(!DeviceIoControl(hIvshmem, ctlCode, nullptr, 0, nullptr, 0, &dwDummyRetLength, nullptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MapMemory() {
|
||||||
|
// Device was opened, let's map memory
|
||||||
|
|
||||||
|
DWORD dwDummyRetLength = 0;
|
||||||
|
|
||||||
|
// Yay, one less syscall.
|
||||||
|
IVSHMEM_MMAP_CONFIG config = { .cacheMode = IVSHMEM_CACHE_WRITECOMBINED };
|
||||||
|
return Ioctl(IOCTL_IVSHMEM_REQUEST_MMAP, config, this->ivshmemMmapStruct);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnmapMemory() {
|
||||||
|
// Unmap memory.
|
||||||
|
if(ivshmemMmapStruct.ptr) {
|
||||||
|
// We do not care if this fails since the device will eventually be closed and this is done only
|
||||||
|
// on agent exit, so the map will be released by the kernel anyhow.
|
||||||
|
static_cast<void>(Ioctl(IOCTL_IVSHMEM_RELEASE_MMAP));
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ivshmemMmapStruct, 0, sizeof(IVSHMEM_MMAP));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Open(const char* pDevName) {
|
||||||
|
if(!OpenDevice())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!MapMemory()) {
|
||||||
|
Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() {
|
||||||
|
UnmapMemory();
|
||||||
|
CloseHandle(hIvshmem);
|
||||||
|
hIvshmem = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GetPointer() {
|
||||||
|
return ivshmemMmapStruct.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
usize GetSize() {
|
||||||
|
return ivshmemMmapStruct.size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace hazelnut
|
78
shared/src/test.cpp
Normal file
78
shared/src/test.cpp
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "atomic_spinlock.hpp"
|
||||||
|
#include "ivshmem.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
struct Test {
|
||||||
|
hazelnut::AtomicSpinlock lk {};
|
||||||
|
std::atomic<u32> sessionId {};
|
||||||
|
std::atomic<u32> pingPong {};
|
||||||
|
};
|
||||||
|
|
||||||
|
hazelnut::IvshmemDevice dev;
|
||||||
|
|
||||||
|
if(!dev.Open("/dev/shm/lg-win7")) {
|
||||||
|
printf("Failed to open ivshmem device\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto size = dev.GetSize();
|
||||||
|
auto ptr = (u32*)dev.GetPointer();
|
||||||
|
|
||||||
|
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
|
||||||
|
|
||||||
|
auto* pTest = (Test*)ptr;
|
||||||
|
u32 last = 0;
|
||||||
|
u32 nrSame = 0;
|
||||||
|
|
||||||
|
u64 initalizedSessionId = pTest->sessionId.load();
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
//printf("%d\n", pTest->timestamp.load());
|
||||||
|
|
||||||
|
if(initalizedSessionId != pTest->sessionId.load()) {
|
||||||
|
printf("Agent restarted. Closing\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need monotonic way
|
||||||
|
#if 0
|
||||||
|
if(auto curTimestamp = pTest->timestamp.load(); curTimestamp < lastTimestamp) {
|
||||||
|
//printf("Not connected or agent crashed %d, %d\n", pTest->timestamp.load() , time(nullptr));
|
||||||
|
printf("Not connected or agent crashed. %zu\n", curTimestamp);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
lastTimestamp = curTimestamp;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// lock for a bit
|
||||||
|
{
|
||||||
|
if(pTest->lk.try_lock_manually()) {
|
||||||
|
// failed to lock
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto current = pTest->pingPong.load();
|
||||||
|
|
||||||
|
if(current == last) {
|
||||||
|
pTest->lk.unlock();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrSame = 0;
|
||||||
|
|
||||||
|
last = current;
|
||||||
|
printf("pingpong %u\n", current);
|
||||||
|
|
||||||
|
pTest->lk.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow the vm some time to do whatever it is it wants to do
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
37
src/atomic_spinlock.rs
Normal file
37
src/atomic_spinlock.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use std::sync::atomic::AtomicU32;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
pub struct AtomicSpinlock {
|
||||||
|
lock: AtomicU32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AtomicSpinlockGuard<'a> {
|
||||||
|
lock: &'a mut AtomicSpinlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AtomicSpinlockGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.lock.store(0, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtomicSpinlock {
|
||||||
|
fn init(&mut self) {
|
||||||
|
self.lock.store(0, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn lock(&mut self) -> AtomicSpinlockGuard<'_> {
|
||||||
|
loop {
|
||||||
|
match self
|
||||||
|
.sync
|
||||||
|
.compare_exchange(cur, 1, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
|
{
|
||||||
|
Ok(last) => return HzLockGuard { lock: self },
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
30
src/ivshmem.rs
Normal file
30
src/ivshmem.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//! Utilities for ivshmem
|
||||||
|
|
||||||
|
use std::sync::atomic::AtomicU32;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
use crate::atomic_spinlock::AtomicSpinlock;
|
||||||
|
|
||||||
|
// at 0x0 in ivshmem map
|
||||||
|
#[repr(C, align(4096))]
|
||||||
|
pub struct HzHeader {
|
||||||
|
magic: AtomicU32,
|
||||||
|
sync: AtomicSpinlock,
|
||||||
|
|
||||||
|
// frame serial. if this is unchanged from prev. load it's not the same
|
||||||
|
frame_serial: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HzHeader {
|
||||||
|
fn valid(&self) -> bool {
|
||||||
|
self.magic.load(std::sync::atomic::Ordering::Acquire) == 0xabcd1234
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HzFrameHeader {
|
||||||
|
width: AtomicU32,
|
||||||
|
height: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Ivshmem {}
|
|
@ -6,6 +6,9 @@ use std::{
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
use minifb::{Window, WindowOptions};
|
use minifb::{Window, WindowOptions};
|
||||||
|
|
||||||
|
mod atomic_spinlock;
|
||||||
|
mod ivshmem;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Message {
|
enum Message {
|
||||||
Resize { width: u32, height: u32 },
|
Resize { width: u32, height: u32 },
|
||||||
|
|
Loading…
Reference in a new issue