diff --git a/pcileech/Android.mk b/pcileech/Android.mk index 41d47ab..71afc67 100644 --- a/pcileech/Android.mk +++ b/pcileech/Android.mk @@ -14,7 +14,7 @@ LOCAL_CFLAGS := -D ANDROID LOCAL_LDLIBS := -L$(LOCAL_PATH)/lib -llog -g LOCAL_C_INCLUDES := bionic -LOCAL_SRC_FILES:= pcileech.c oscompatibility.c device.c device3380.c device605.c executor.c extra.c help.c kmd.c memdump.c mempatch.c statistics.c tlp.c util.c vfs.c +LOCAL_SRC_FILES:= pcileech.c oscompatibility.c device.c device3380.c device605_uart.c device605_601.c executor.c extra.c help.c kmd.c memdump.c mempatch.c statistics.c tlp.c util.c vfs.c LOCAL_MODULE := pcileech LOCAL_SHARED_LIBRARIES += libusb1.0 diff --git a/pcileech/Makefile b/pcileech/Makefile index 103eac7..b0aef7d 100644 --- a/pcileech/Makefile +++ b/pcileech/Makefile @@ -1,7 +1,7 @@ CC=gcc CFLAGS=-I. -D LINUX -pthread `pkg-config libusb-1.0 --libs --cflags` DEPS = pcileech.h -OBJ = pcileech oscompatibility.o pcileech.o device.o device3380.o device605.o executor.o extra.o help.o kmd.o memdump.o mempatch.o statistics.o tlp.o util.o vfs.o +OBJ = pcileech oscompatibility.o pcileech.o device.o device3380.o device605_uart.o device605_601.o executor.o extra.o help.o kmd.o memdump.o mempatch.o statistics.o tlp.o util.o vfs.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/pcileech/device.c b/pcileech/device.c index f808cdf..afc560b 100644 --- a/pcileech/device.c +++ b/pcileech/device.c @@ -5,20 +5,57 @@ // #include "device.h" #include "kmd.h" +#include "statistics.h" #include "device3380.h" -#include "device605.h" +#include "device605_uart.h" +#include "device605_601.h" BOOL DeviceReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) { if(flags & PCILEECH_MEM_FLAG_RETRYONFAIL) { return DeviceReadDMA(ctx, qwAddr, pb, cb, 0) || DeviceReadDMA(ctx, qwAddr, pb, cb, 0); } - if(PCILEECH_DEVICE_USB3380 == ctx->cfg->tpDevice) { - return Device3380_ReadDMA(ctx, qwAddr, pb, cb); - } else if(PCILEECH_DEVICE_SP605 == ctx->cfg->tpDevice) { - return Device605_ReadDMA(ctx, qwAddr, pb, cb); + return ctx->cfg->dev.pfnReadDMA ? ctx->cfg->dev.pfnReadDMA(ctx, qwAddr, pb, cb) : FALSE; +} + +#define CHUNK_FAIL_DIVISOR 16 +DWORD DeviceReadDMAEx_DoWork(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _Inout_opt_ PPAGE_STATISTICS pPageStat, _In_ DWORD cbMaxSizeIo) +{ + DWORD cbRd, cbRdOff; + DWORD cbChunk, cChunkTotal, cChunkSuccess = 0; + DWORD i, cbSuccess = 0; + // calculate current chunk sizes + cbChunk = ~0xfff & min(cb, cbMaxSizeIo); + cbChunk = (cbChunk > 0x3000) ? cbChunk : 0x1000; + cChunkTotal = (cb / cbChunk) + ((cb % cbChunk) ? 1 : 0); + // try read memory + memset(pb, 0, cb); + for(i = 0; i < cChunkTotal; i++) { + cbRdOff = i * cbChunk; + cbRd = ((i == cChunkTotal - 1) && (cb % cbChunk)) ? (cb % cbChunk) : cbChunk; // (last chunk may be smaller) + if(DeviceReadDMA(ctx, qwAddr + cbRdOff, pb + cbRdOff, cbRd, 0)) { + cbSuccess += cbRd; + PageStatUpdate(pPageStat, qwAddr + cbRdOff + cbRd, cbRd / 0x1000, 0); + } else if(cbRd == 0x1000) { + PageStatUpdate(pPageStat, qwAddr + cbRdOff + cbRd, 0, 1); + } else { + cbSuccess += DeviceReadDMAEx_DoWork(ctx, qwAddr + cbRdOff, pb + cbRdOff, cbRd, pPageStat, cbRd / CHUNK_FAIL_DIVISOR); + } + } + return cbSuccess; +} + +DWORD DeviceReadDMAEx(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _Inout_opt_ PPAGE_STATISTICS pPageStat) +{ + BYTE pbWorkaround[4096]; + DWORD cbWorkaround; + if(cb != 0x1000) { + return DeviceReadDMAEx_DoWork(ctx, qwAddr, pb, cb, pPageStat, (DWORD)ctx->cfg->qwMaxSizeDmaIo); } - return FALSE; + // why is this working ??? if not here console is screwed up... (threading issue?) + cbWorkaround = DeviceReadDMAEx_DoWork(ctx, qwAddr, pbWorkaround, 0x1000, pPageStat, (DWORD)ctx->cfg->qwMaxSizeDmaIo); + memcpy(pb, pbWorkaround, 0x1000); + return cbWorkaround; } BOOL DeviceWriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) @@ -28,11 +65,7 @@ BOOL DeviceWriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE if(flags & PCILEECH_MEM_FLAG_RETRYONFAIL) { return DeviceWriteDMA(ctx, qwAddr, pb, cb, 0) || DeviceWriteDMA(ctx, qwAddr, pb, cb, 0); } - if(PCILEECH_DEVICE_USB3380 == ctx->cfg->tpDevice) { - result = Device3380_WriteDMA(ctx, qwAddr, pb, cb); - } else if(PCILEECH_DEVICE_SP605 == ctx->cfg->tpDevice) { - result = Device605_WriteDMA(ctx, qwAddr, pb, cb); - } + result = ctx->cfg->dev.pfnWriteDMA ? ctx->cfg->dev.pfnWriteDMA(ctx, qwAddr, pb, cb) : FALSE; if(!result) { return FALSE; } if(flags & PCILEECH_MEM_FLAG_VERIFYWRITE) { pbV = LocalAlloc(0, cb + 0x2000); @@ -47,32 +80,43 @@ BOOL DeviceWriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE BOOL DeviceProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap) { - if(PCILEECH_DEVICE_SP605 == ctx->cfg->tpDevice) { - Device605_ProbeDMA(ctx, qwAddr, cPages, pbResultMap); - return TRUE; - } - return FALSE; + if(!ctx->cfg->dev.pfnProbeDMA) { return FALSE; } + ctx->cfg->dev.pfnProbeDMA(ctx, qwAddr, cPages, pbResultMap); + return TRUE; +} + +BOOL DeviceWriteTlp(_Inout_ PPCILEECH_CONTEXT ctx, _In_ PBYTE pb, _In_ DWORD cb) +{ + if(!ctx->cfg->dev.pfnWriteTlp) { return FALSE; } + return ctx->cfg->dev.pfnWriteTlp(ctx, pb, cb); +} + +BOOL DeviceListenTlp(_Inout_ PPCILEECH_CONTEXT ctx, _In_ DWORD dwTime) +{ + if(!ctx->cfg->dev.pfnListenTlp) { return FALSE; } + return ctx->cfg->dev.pfnListenTlp(ctx, dwTime); } VOID DeviceClose(_Inout_ PPCILEECH_CONTEXT ctx) { - if(ctx->hDevice) { - if(PCILEECH_DEVICE_USB3380 == ctx->cfg->tpDevice) { - Device3380_Close(ctx); - } else if(PCILEECH_DEVICE_SP605 == ctx->cfg->tpDevice) { - Device605_Close(ctx); - } + if(ctx->hDevice && ctx->cfg->dev.pfnClose) { + ctx->cfg->dev.pfnClose(ctx); } } BOOL DeviceOpen(_Inout_ PPCILEECH_CONTEXT ctx) { - if(PCILEECH_DEVICE_USB3380 == ctx->cfg->tpDevice) { - return Device3380_Open(ctx); - } else if(PCILEECH_DEVICE_SP605 == ctx->cfg->tpDevice) { - return Device605_Open(ctx); + BOOL result = FALSE; + if(PCILEECH_DEVICE_USB3380 == ctx->cfg->dev.tp || PCILEECH_DEVICE_NA == ctx->cfg->dev.tp) { + result = Device3380_Open(ctx); } - return FALSE; + if(PCILEECH_DEVICE_SP605_FT601 == ctx->cfg->dev.tp || PCILEECH_DEVICE_NA == ctx->cfg->dev.tp) { + result = Device605_601_Open(ctx); + } + if(PCILEECH_DEVICE_SP605_UART == ctx->cfg->dev.tp) { + result = Device605_UART_Open(ctx); + } + return result; } BOOL DeviceWriteMEM(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) @@ -88,7 +132,9 @@ BOOL DeviceReadMEM(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE { if(ctx->phKMD) { return KMDReadMemory(ctx, qwAddr, pb, cb); - } else { + } else if(flags || cb == 0x1000) { return DeviceReadDMA(ctx, qwAddr, pb, cb, flags); + } else { + return cb == DeviceReadDMAEx(ctx, qwAddr, pb, cb, NULL); } } diff --git a/pcileech/device.h b/pcileech/device.h index 17d8099..416dec8 100644 --- a/pcileech/device.h +++ b/pcileech/device.h @@ -6,6 +6,7 @@ #ifndef __DEVICE_H__ #define __DEVICE_H__ #include "pcileech.h" +#include "statistics.h" #define PCILEECH_MEM_FLAG_RETRYONFAIL 0x01 #define PCILEECH_MEM_FLAG_VERIFYWRITE 0x02 @@ -34,6 +35,20 @@ VOID DeviceClose(_Inout_ PPCILEECH_CONTEXT ctx); */ BOOL DeviceReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags); +/* +* Try read memory with DMA in a fairly optimal way considering device limits. +* The number of total successfully read bytes is returned. Failed reads will +* be zeroed out the he returned memory. +* -- ctx +* -- qwAddr +* -- pb +* -- cb +* -- pPageStat = optional page statistics +* -- return = the number of bytes successfully read. +* +*/ +DWORD DeviceReadDMAEx(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _Inout_opt_ PPAGE_STATISTICS pPageStat); + /* * Write data to the target system using DMA. * -- ctx @@ -82,4 +97,22 @@ BOOL DeviceWriteMEM(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE */ BOOL DeviceReadMEM(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags); +/* +* Write PCIe Transaction Layer Packets (TLPs) to the device. +* -- ctx +* -- pb = PCIe TLP/TLPs to send. +* -- cb = +* -- return +*/ +BOOL DeviceWriteTlp(_Inout_ PPCILEECH_CONTEXT ctx, _In_ PBYTE pb, _In_ DWORD cb); + +/* +* Listen for incoming PCIe Transaction Layer Packets (TLPs) for a specific +* amount of time. +* -- ctx +* -- dwTime = time in ms +* -- return +*/ +BOOL DeviceListenTlp(_Inout_ PPCILEECH_CONTEXT ctx, _In_ DWORD dwTime); + #endif /* __DEVICE_H__ */ \ No newline at end of file diff --git a/pcileech/device3380.c b/pcileech/device3380.c index c46fdec..be6f16b 100644 --- a/pcileech/device3380.c +++ b/pcileech/device3380.c @@ -296,7 +296,7 @@ BOOL Device3380_FlashEEPROM(_Inout_ PPCILEECH_CONTEXT ctx, _In_ PBYTE pbEEPROM, VOID Action_Device3380_Flash(_Inout_ PPCILEECH_CONTEXT ctx) { BOOL result; - if(ctx->cfg->tpDevice != PCILEECH_DEVICE_USB3380) { + if(ctx->cfg->dev.tp != PCILEECH_DEVICE_USB3380) { printf("Flash failed: unsupported device.\n"); return; } @@ -320,7 +320,7 @@ VOID Action_Device3380_Flash(_Inout_ PPCILEECH_CONTEXT ctx) VOID Action_Device3380_8051Start(_Inout_ PPCILEECH_CONTEXT ctx) { BOOL result; - if(ctx->cfg->tpDevice != PCILEECH_DEVICE_USB3380) { + if(ctx->cfg->dev.tp != PCILEECH_DEVICE_USB3380) { printf("8051 startup failed: unsupported device.\n"); return; } @@ -339,7 +339,7 @@ VOID Action_Device3380_8051Start(_Inout_ PPCILEECH_CONTEXT ctx) VOID Action_Device3380_8051Stop(_Inout_ PPCILEECH_CONTEXT ctx) { - if(ctx->cfg->tpDevice != PCILEECH_DEVICE_USB3380) { + if(ctx->cfg->dev.tp != PCILEECH_DEVICE_USB3380) { printf("Stopping 8051 failed: unsupported device.\n"); return; } @@ -376,6 +376,18 @@ BOOL DevicePciInReadDma(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ BOOL Device3380_Open2(_Inout_ PPCILEECH_CONTEXT ctx); +VOID Device3380_Close(_Inout_ PPCILEECH_CONTEXT ctx) +{ + PDEVICE_DATA pDeviceData = (PDEVICE_DATA)ctx->hDevice; + if(!pDeviceData) { return; } + if(!pDeviceData->HandlesOpen) { return; } + WinUsb_Free(pDeviceData->WinusbHandle); + if(pDeviceData->DeviceHandle) { CloseHandle(pDeviceData->DeviceHandle); } + pDeviceData->HandlesOpen = FALSE; + LocalFree(ctx->hDevice); + ctx->hDevice = 0; +} + BOOL Device3380_Open(_Inout_ PPCILEECH_CONTEXT ctx) { BOOL result; @@ -384,7 +396,7 @@ BOOL Device3380_Open(_Inout_ PPCILEECH_CONTEXT ctx) if(!result) { return FALSE; } Device3380_ReadCsr((PDEVICE_DATA)ctx->hDevice, REG_USBSTAT, &dwReg, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); if(ctx->cfg->fForceUsb2 && (dwReg & 0x0100 /* Super-Speed(USB3) */)) { - printf("Device Info: Device running at USB3 speed; downgrading to USB2 ...\n"); + printf("Device Info: USB3380 running at USB3 speed; downgrading to USB2 ...\n"); dwReg = 0x04; // USB2=ENABLE, USB3=DISABLE Device3380_WriteCsr((PDEVICE_DATA)ctx->hDevice, REG_USBCTL2, dwReg, CSR_CONFIGSPACE_MEMM | CSR_BYTE0); Device3380_Close(ctx); @@ -394,26 +406,22 @@ BOOL Device3380_Open(_Inout_ PPCILEECH_CONTEXT ctx) Device3380_ReadCsr((PDEVICE_DATA)ctx->hDevice, REG_USBSTAT, &dwReg, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); } if(dwReg & 0xc0 /* Full-Speed(USB1)|High-Speed(USB2) */) { - printf("Device Info: Device running at USB2 speed.\n"); + printf("Device Info: USB330 running at USB2 speed.\n"); } else if(ctx->cfg->fVerbose) { - printf("Device Info: Device running at USB3 speed.\n"); + printf("Device Info: USB330 running at USB3 speed.\n"); } if(ctx->cfg->fVerbose) { printf("Device Info: USB3380.\n"); } + // set callback functions and fix up config + ctx->cfg->dev.tp = PCILEECH_DEVICE_USB3380; + ctx->cfg->dev.qwMaxSizeDmaIo = 0x01000000; + ctx->cfg->dev.qwAddrMaxNative = 0x00000000ffffffff; + ctx->cfg->dev.fPartialPageReadSupported = TRUE; + ctx->cfg->dev.pfnClose = Device3380_Close; + ctx->cfg->dev.pfnReadDMA = Device3380_ReadDMA; + ctx->cfg->dev.pfnWriteDMA = Device3380_WriteDMA; return TRUE; } -VOID Device3380_Close(_Inout_ PPCILEECH_CONTEXT ctx) -{ - PDEVICE_DATA pDeviceData = (PDEVICE_DATA)ctx->hDevice; - if(!pDeviceData) { return; } - if(!pDeviceData->HandlesOpen) { return; } - WinUsb_Free(pDeviceData->WinusbHandle); - if(pDeviceData->DeviceHandle) { CloseHandle(pDeviceData->DeviceHandle); } - pDeviceData->HandlesOpen = FALSE; - LocalFree(ctx->hDevice); - ctx->hDevice = 0; -} - #ifdef WIN32 #include diff --git a/pcileech/device3380.h b/pcileech/device3380.h index 95abc42..fa028bc 100644 --- a/pcileech/device3380.h +++ b/pcileech/device3380.h @@ -14,32 +14,6 @@ */ BOOL Device3380_Open(_Inout_ PPCILEECH_CONTEXT ctx); -/* -* Clean up various device related stuff and deallocate memory buffers. -* -- ctx -*/ -VOID Device3380_Close(_Inout_ PPCILEECH_CONTEXT ctx); - -/* -* Read data from the target system using DMA. -* -- ctx -* -- qwAddr - max supported address = 0x100000000 - cb - (32-bit address space) -* -- pb -* -- cb -* -- return -*/ -BOOL Device3380_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb); - -/* -* Write data to the target system using DMA. -* -- ctx -* -- qwAddr - max supported address = 0x100000000 - cb - (32-bit address space) -* -- pb -* -- cb -* -- return -*/ -BOOL Device3380_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb); - /* * Flash a new firmware into the onboard memory of the USB3380 card. * This may be dangerious and the device may stop working after a reflash! diff --git a/pcileech/device605.h b/pcileech/device605.h deleted file mode 100644 index 78dab3f..0000000 --- a/pcileech/device605.h +++ /dev/null @@ -1,59 +0,0 @@ -// device605.h : definitions related to the Xilinx SP605 dev board flashed with @d_olex early access bitstream. (UART communication). -// -// (c) Ulf Frisk, 2017 -// Author: Ulf Frisk, pcileech@frizk.net -// -#ifndef __DEVICE605_H__ -#define __DEVICE605_H__ -#include "pcileech.h" - -/* -* Open a connection to the SP605 PCILeech flashed device. -* -- ctx -* -- result -*/ -BOOL Device605_Open(_Inout_ PPCILEECH_CONTEXT ctx); - -/* -* Clean up various device related stuff and deallocate memory buffers. -* -- ctx -*/ -VOID Device605_Close(_Inout_ PPCILEECH_CONTEXT ctx); - -/* -* Read data from the target system using DMA. -* -- ctx -* -- qwAddr -* -- pb -* -- cb -* -- return -*/ -BOOL Device605_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb); - -/* -* Write data to the target system using DMA. -* -- ctx -* -- qwAddr -* -- pb -* -- cb -* -- return -*/ -BOOL Device605_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb); - -/* -* Probe the memory of the target system to check whether it's readable or not. -* -- ctx -* -- qwAddr = address to start probe from. -* -- cPages = number of 4kB pages to probe. -* -- pbResultMap = result map, 1 byte represents 1 page, 0 = fail, 1 = success. -* -- return = FALSE if not supported by underlying hardware, TRUE if supported. -*/ -VOID Device605_ProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap); - -/* -* Transmit a raw PCIe TLP. -* -- ctx -*/ -VOID Action_Device605_TlpTx(_Inout_ PPCILEECH_CONTEXT ctx); - -#endif /* __DEVICE605_H__ */ diff --git a/pcileech/device605_601.c b/pcileech/device605_601.c new file mode 100644 index 0000000..3172a53 --- /dev/null +++ b/pcileech/device605_601.c @@ -0,0 +1,488 @@ +// device605_601.c : implementation related to the Xilinx SP605 dev board flashed with bitstream for FTDI UMFT601X-B addon-board. +// +// (c) Ulf Frisk, 2017 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifdef WIN32 + +#include "device605_601.h" +#include "device.h" +#include "tlp.h" +#include "util.h" + +//------------------------------------------------------------------------------- +// FPGA/SP605/FT601 defines below. +//------------------------------------------------------------------------------- + +#define FPGA_CFG_RX_VALID 0x77000000 +#define FPGA_CFG_RX_VALID_MASK 0xff070000 +#define FPGA_CMD_RX_VALID 0x77020000 +#define FPGA_CMD_RX_VALID_MASK 0xff070000 + +#define FPGA_TLP_RX_VALID 0x77030000 +#define FPGA_TLP_RX_VALID_MASK 0xff030000 +#define FPGA_TLP_RX_VALID_LAST 0x77070000 +#define FPGA_TLP_RX_VALID_LAST_MASK 0xff070000 + +#define FPGA_TLP_TX_VALID 0x77030000 +#define FPGA_TLP_TX_VALID_LAST 0x77070000 +#define FPGA_LOOP_TX_VALID 0x77010000 + +#define SP605_601_PROBE_MAXPAGES 0x400 +#define SP601_601_MAX_SIZE_RX 0x001e000 // in data bytes (excl. overhead/TLP headers) +#define SP601_601_MAX_SIZE_TX 0x0002000 // in total data (incl. overhead/TLP headers) + +#define ENDIAN_SWAP_WORD(x) (x = (x << 8) | (x >> 8)) +#define ENDIAN_SWAP_DWORD(x) (x = (x << 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x >> 24)) + +typedef struct tdDEVICE_CONTEXT_SP605_601 { + WORD wDeviceId; + WORD wFpgaVersion; + BOOL isPrintTlp; + PTLP_CALLBACK_BUF_MRd pMRdBuffer; + struct { + PBYTE pb; + DWORD cb; + DWORD cbMax; + } rxbuf; + struct { + PBYTE pb; + DWORD cb; + DWORD cbMax; + } txbuf; + struct { + HMODULE hModule; + HANDLE hFTDI; + ULONG(*pfnFT_Create)( + PVOID pvArg, + DWORD dwFlags, + HANDLE *pftHandle + ); + ULONG(*pfnFT_Close)( + HANDLE ftHandle + ); + ULONG(*pfnFT_WritePipe)( + HANDLE ftHandle, + UCHAR ucPipeID, + PUCHAR pucBuffer, + ULONG ulBufferLength, + PULONG pulBytesTransferred, + LPOVERLAPPED pOverlapped + ); + ULONG(*pfnFT_ReadPipe)( + HANDLE ftHandle, + UCHAR ucPipeID, + PUCHAR pucBuffer, + ULONG ulBufferLength, + PULONG pulBytesTransferred, + LPOVERLAPPED pOverlapped + ); + ULONG(*pfnFT_AbortPipe)( + HANDLE ftHandle, + UCHAR ucPipeID + ); + + } dev; + BOOL(*hRxTlpCallbackFn)(_Inout_ PTLP_CALLBACK_BUF_MRd pBufferMrd, _In_ PBYTE pb, _In_ DWORD cb, _In_opt_ HANDLE hEventCompleted); + QWORD dbg_qwLastTx[8]; + DWORD dbg_cbLastTx; +} DEVICE_CONTEXT_SP605_601, *PDEVICE_CONTEXT_SP605_601; + +//------------------------------------------------------------------------------- +// FPGA/SP605/FT601 implementation below. +//------------------------------------------------------------------------------- + +VOID Device601_601_InitializeFTDI(_In_ PDEVICE_CONTEXT_SP605_601 ctx) +{ + DWORD status; + // Load FTDI Library + ctx->dev.hModule = LoadLibrary(L"FTD3XX.dll"); + if(!ctx->dev.hModule) { return; } + ctx->dev.pfnFT_AbortPipe = (ULONG(*)(HANDLE, UCHAR)) + GetProcAddress(ctx->dev.hModule, "FT_AbortPipe"); + ctx->dev.pfnFT_Close = (ULONG(*)(HANDLE)) + GetProcAddress(ctx->dev.hModule, "FT_Close"); + ctx->dev.pfnFT_Create = (ULONG(*)(PVOID, DWORD, HANDLE*)) + GetProcAddress(ctx->dev.hModule, "FT_Create"); + ctx->dev.pfnFT_ReadPipe = (ULONG(*)(HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)) + GetProcAddress(ctx->dev.hModule, "FT_ReadPipe"); + ctx->dev.pfnFT_WritePipe = (ULONG(*)(HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)) + GetProcAddress(ctx->dev.hModule, "FT_WritePipe"); + // Open FTDI + status = ctx->dev.pfnFT_Create(NULL, 0x10 /*FT_OPEN_BY_INDEX*/, &ctx->dev.hFTDI); + if(status || !ctx->dev.hFTDI) { return; } + ctx->dev.pfnFT_AbortPipe(ctx->dev.hFTDI, 0x02); + ctx->dev.pfnFT_AbortPipe(ctx->dev.hFTDI, 0x82); +} + +VOID Device605_601_Close(_Inout_ PPCILEECH_CONTEXT ctxPcileech) +{ + PDEVICE_CONTEXT_SP605_601 ctx = (PDEVICE_CONTEXT_SP605_601)ctxPcileech->hDevice; + if(!ctx) { return; } + if(ctx->dev.hFTDI) { ctx->dev.pfnFT_Close(ctx->dev.hFTDI); } + if(ctx->dev.hModule) { FreeLibrary(ctx->dev.hModule); } + if(ctx->rxbuf.pb) { LocalFree(ctx->rxbuf.pb); } + if(ctx->txbuf.pb) { LocalFree(ctx->txbuf.pb); } + LocalFree(ctx); + ctxPcileech->hDevice = 0; +} + +VOID Device605_601_GetDeviceID_FpgaVersion(_In_ PDEVICE_CONTEXT_SP605_601 ctx) +{ + DWORD status; + DWORD cbTX, cbRX, i, dwStatus, dwData; + PBYTE pbRX = LocalAlloc(0, 0x01000000); + BYTE pbTX[24] = { + // cfg read addr 0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + // cmd msg: version + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x77, + // mirror msg -> at least something to read back -> no ft601 freeze. + 0xff, 0xee, 0xdd, 0xcc, 0x00, 0x00, 0x01, 0x77 + }; + status = ctx->dev.pfnFT_WritePipe(ctx->dev.hFTDI, 0x02, pbTX, 24, &cbTX, NULL); + if(status) { goto fail; } + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbRX, 0x01000000, &cbRX, NULL); + if(status || cbRX < 16) { goto fail; } + for(i = 0; i < cbRX - 7; i += 8) { + dwData = *(PDWORD)(pbRX + i); + dwStatus = *(PDWORD)(pbRX + i + 4); + if(!ctx->wDeviceId && (FPGA_CFG_RX_VALID == (FPGA_CFG_RX_VALID_MASK & dwStatus))) { + ctx->wDeviceId = dwStatus & 0xffff; + } + if(!ctx->wFpgaVersion && (FPGA_CMD_RX_VALID == (FPGA_CMD_RX_VALID_MASK & dwStatus))) { + ctx->wFpgaVersion = dwData & 0xffff; + ENDIAN_SWAP_WORD(ctx->wFpgaVersion); + } + } +fail: + LocalFree(pbRX); +} + +BOOL Device605_601_TxTlp(_In_ PDEVICE_CONTEXT_SP605_601 ctx, _In_ PBYTE pbTlp, _In_ DWORD cbTlp, BOOL fRdKeepalive, BOOL fFlush) +{ + DWORD status; + PBYTE pbTx; + DWORD i, cbTx, cbTxed = 0; + if(cbTlp & 0x3) { return FALSE; } + if(cbTlp > 2048) { return FALSE; } + if(ctx->isPrintTlp) { + TLP_Print(pbTlp, cbTlp, TRUE); + } + // prepare transmit buffer + pbTx = ctx->txbuf.pb + ctx->txbuf.cb; + cbTx = 2 * cbTlp; + for(i = 0; i < cbTlp; i += 4) { + *(PDWORD)(pbTx + (i << 1)) = *(PDWORD)(pbTlp + i); + *(PDWORD)(pbTx + ((i << 1) + 4)) = FPGA_TLP_TX_VALID; + } + if(cbTlp) { + *(PDWORD)(pbTx + ((i << 1) - 4)) = FPGA_TLP_TX_VALID_LAST; + } + if(fRdKeepalive) { + cbTx += 8; + *(PDWORD)(pbTx + (i << 1)) = 0xffeeddcc; + *(PDWORD)(pbTx + ((i << 1) + 4)) = FPGA_LOOP_TX_VALID; + } + ctx->txbuf.cb += cbTx; + // transmit + if((ctx->txbuf.cb > SP601_601_MAX_SIZE_TX) || (fFlush && ctx->txbuf.cb)) { + status = ctx->dev.pfnFT_WritePipe(ctx->dev.hFTDI, 0x02, ctx->txbuf.pb, ctx->txbuf.cb, &cbTxed, NULL); + ctx->txbuf.cb = 0; + return (0 == status); + } + return TRUE; +} + +#define TLP_RX_MAX_SIZE 1024 +VOID Device605_601_RxTlpSynchronous(_In_ PDEVICE_CONTEXT_SP605_601 ctx) +{ + DWORD status; + DWORD dwTlp, dwStatus; + DWORD i, cdwTlp = 0; + BYTE pbTlp[TLP_RX_MAX_SIZE]; + PDWORD pdwTlp = (PDWORD)pbTlp; + PDWORD pdwRx = (PDWORD)ctx->rxbuf.pb; + + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb, ctx->rxbuf.cbMax, &ctx->rxbuf.cb, NULL); + if(status) { + ctx->dev.pfnFT_AbortPipe(ctx->dev.hFTDI, 0x82); + return; + } + if(ctx->rxbuf.cb % 8) { + printf("Device Info: SP605 / FT601: Bad read from device. Should not happen!\n"); + return; + } + for(i = 0; i < ctx->rxbuf.cb / sizeof(QWORD); i++) { // index in 64-bit (QWORD) + dwTlp = pdwRx[i << 1]; + dwStatus = pdwRx[1 + (i << 1)]; + if(FPGA_TLP_RX_VALID == (FPGA_TLP_RX_VALID_MASK & dwStatus)) { + pdwTlp[cdwTlp] = dwTlp; + cdwTlp++; + if(cdwTlp >= TLP_RX_MAX_SIZE / sizeof(DWORD)) { return; } + } + if(FPGA_TLP_RX_VALID_LAST == (FPGA_TLP_RX_VALID_LAST_MASK & dwStatus)) { + if(cdwTlp >= 3) { + if(ctx->isPrintTlp) { + TLP_Print(pbTlp, cdwTlp << 2, FALSE); + } + if(ctx->hRxTlpCallbackFn) { + ctx->hRxTlpCallbackFn(ctx->pMRdBuffer, pbTlp, cdwTlp << 2, NULL); + } + } else { + printf("Device Info: SP605 / FT601: Bad PCIe TLP received! Should not happen!\n"); + } + cdwTlp = 0; + } + } +} + +BOOL Device605_601_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb) +{ + PDEVICE_CONTEXT_SP605_601 ctx = (PDEVICE_CONTEXT_SP605_601)ctxPcileech->hDevice; + TLP_CALLBACK_BUF_MRd rxbuf; + DWORD tx[4], o, i; + BOOL is32; + PTLP_HDR_MRdWr64 hdrRd64 = (PTLP_HDR_MRdWr64)tx; + PTLP_HDR_MRdWr32 hdrRd32 = (PTLP_HDR_MRdWr32)tx; + if(cb > SP601_601_MAX_SIZE_RX) { return FALSE; } + if(qwAddr % 0x1000) { return FALSE; } + if((cb >= 0x1000) && (cb % 0x1000)) { return FALSE; } + if((cb < 0x1000) && (cb % 0x8)) { return FALSE; } + // prepare + rxbuf.cb = 0; + rxbuf.pb = pb; + rxbuf.cbMax = cb; + ctx->pMRdBuffer = &rxbuf; + ctx->hRxTlpCallbackFn = TLP_CallbackMRd; + // transmit TLPs + for(o = 0; o < cb; o += 0x1000) { + memset(tx, 0, 16); + is32 = qwAddr + o < 0x100000000; + if(is32) { + hdrRd32->h.TypeFmt = TLP_MRd32; + hdrRd32->h.Length = (WORD)((cb < 0x1000) ? cb >> 2 : 0); + hdrRd32->RequesterID = ctx->wDeviceId; + hdrRd32->Tag = (BYTE)(o >> 12); + hdrRd32->FirstBE = 0xf; + hdrRd32->LastBE = 0xf; + hdrRd32->Address = (DWORD)(qwAddr + o); + } + else { + hdrRd64->h.TypeFmt = TLP_MRd64; + hdrRd64->h.Length = (WORD)((cb < 0x1000) ? cb >> 2 : 0); + hdrRd64->RequesterID = ctx->wDeviceId; + hdrRd64->Tag = (BYTE)(o >> 12); + hdrRd64->FirstBE = 0xf; + hdrRd64->LastBE = 0xf; + hdrRd64->AddressHigh = (DWORD)((qwAddr + o) >> 32); + hdrRd64->AddressLow = (DWORD)(qwAddr + o); + } + for(i = 0; i < 4; i++) { + ENDIAN_SWAP_DWORD(tx[i]); + } + Device605_601_TxTlp(ctx, (PBYTE)tx, is32 ? 12 : 16, TRUE, (o % 0x8000 == 0x7000)); + } + Device605_601_TxTlp(ctx, NULL, 0, TRUE, TRUE); + usleep(300); + Device605_601_RxTlpSynchronous(ctx); + ctx->pMRdBuffer = NULL; + return rxbuf.cb >= rxbuf.cbMax; +} + +VOID Device605_601_ProbeDMA(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap) +{ + DWORD i, j; + PDEVICE_CONTEXT_SP605_601 ctx = (PDEVICE_CONTEXT_SP605_601)ctxPcileech->hDevice; + TLP_CALLBACK_BUF_MRd bufMRd; + DWORD tx[4]; + BOOL is32; + PTLP_HDR_MRdWr64 hdrRd64 = (PTLP_HDR_MRdWr64)tx; + PTLP_HDR_MRdWr32 hdrRd32 = (PTLP_HDR_MRdWr32)tx; + // split probe into processing chunks if too large... + while(cPages > SP605_601_PROBE_MAXPAGES) { + Device605_601_ProbeDMA(ctxPcileech, qwAddr, SP605_601_PROBE_MAXPAGES, pbResultMap); + cPages -= SP605_601_PROBE_MAXPAGES; + pbResultMap += SP605_601_PROBE_MAXPAGES; + qwAddr += SP605_601_PROBE_MAXPAGES << 12; + } + memset(pbResultMap, 0, cPages); + // prepare + bufMRd.cb = 0; + bufMRd.pb = pbResultMap; + bufMRd.cbMax = cPages; + ctx->pMRdBuffer = &bufMRd; + ctx->hRxTlpCallbackFn = TLP_CallbackMRdProbe; + // transmit TLPs + for(i = 0; i < cPages; i++) { + memset(tx, 0, 16); + is32 = qwAddr + (i << 12) < 0x100000000; + if(is32) { + hdrRd32->h.TypeFmt = TLP_MRd32; + hdrRd32->h.Length = 1; + hdrRd32->RequesterID = ctx->wDeviceId; + hdrRd32->FirstBE = 0xf; + hdrRd32->LastBE = 0; + hdrRd32->Address = (DWORD)(qwAddr + (i << 12) + ((i & 0x1f) << 2)); // 5 low address bits coded into the dword read. + hdrRd32->Tag = (BYTE)((i >> 5) & 0x1f); // 5 high address bits coded into tag. + } else { + hdrRd64->h.TypeFmt = TLP_MRd64; + hdrRd64->h.Length = 1; + hdrRd64->RequesterID = ctx->wDeviceId; + hdrRd64->FirstBE = 0xf; + hdrRd64->LastBE = 0; + hdrRd64->AddressHigh = (DWORD)((qwAddr + (i << 12)) >> 32); + hdrRd64->AddressLow = (DWORD)(qwAddr + (i << 12) + ((i & 0x1f) << 2)); // 5 low address bits coded into the dword read. + hdrRd64->Tag = (BYTE)((i >> 5) & 0x1f); // 5 high address bits coded into tag. + } + for(j = 0; j < 4; j++) { + ENDIAN_SWAP_DWORD(tx[j]); + } + Device605_601_TxTlp(ctx, (PBYTE)tx, is32 ? 12 : 16, FALSE, (i % 24 == 0)); + } + Device605_601_TxTlp(ctx, NULL, 0, TRUE, TRUE); + usleep(300); + Device605_601_RxTlpSynchronous(ctx); + ctx->hRxTlpCallbackFn = NULL; + ctx->pMRdBuffer = NULL; +} + +// write max 128 byte packets. +BOOL Device605_601_WriteDMA_TXP(_Inout_ PDEVICE_CONTEXT_SP605_601 ctx, _In_ QWORD qwA, _In_ BYTE bFirstBE, _In_ BYTE bLastBE, _In_ PBYTE pb, _In_ DWORD cb) +{ + DWORD txbuf[36], i, cbTlp; + PBYTE pbTlp = (PBYTE)txbuf; + PTLP_HDR_MRdWr32 hdrWr32 = (PTLP_HDR_MRdWr32)txbuf; + PTLP_HDR_MRdWr64 hdrWr64 = (PTLP_HDR_MRdWr64)txbuf; + memset(pbTlp, 0, 16); + if(qwA < 0x100000000) { + hdrWr32->h.TypeFmt = TLP_MWr32; + hdrWr32->h.Length = (WORD)(cb + 3) >> 2; + hdrWr32->FirstBE = bFirstBE; + hdrWr32->LastBE = bLastBE; + hdrWr32->RequesterID = ctx->wDeviceId; + hdrWr32->Address = (DWORD)qwA; + for(i = 0; i < 3; i++) { + ENDIAN_SWAP_DWORD(txbuf[i]); + } + memcpy(pbTlp + 12, pb, cb); + cbTlp = (12 + cb + 3) & ~0x3; + } else { + hdrWr64->h.TypeFmt = TLP_MWr64; + hdrWr64->h.Length = (WORD)(cb + 3) >> 2; + hdrWr64->FirstBE = bFirstBE; + hdrWr64->LastBE = bLastBE; + hdrWr64->RequesterID = ctx->wDeviceId; + hdrWr64->AddressHigh = (DWORD)(qwA >> 32); + hdrWr64->AddressLow = (DWORD)qwA; + for(i = 0; i < 4; i++) { + ENDIAN_SWAP_DWORD(txbuf[i]); + } + memcpy(pbTlp + 16, pb, cb); + cbTlp = (16 + cb + 3) & ~0x3; + } + return Device605_601_TxTlp(ctx, pbTlp, cbTlp, FALSE, FALSE); +} + +BOOL Device605_601_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ QWORD qwA, _In_ PBYTE pb, _In_ DWORD cb) +{ + PDEVICE_CONTEXT_SP605_601 ctx = (PDEVICE_CONTEXT_SP605_601)ctxPcileech->hDevice; + BOOL result = TRUE; + BYTE be, pbb[4]; + DWORD cbtx; + // TX 1st dword if not aligned + if(cb && (qwA & 0x3)) { + be = (cb < 3) ? (0xf >> (4 - cb)) : 0xf; + be <<= qwA & 0x3; + cbtx = min(cb, 4 - (qwA & 0x3)); + memcpy(pbb + (qwA & 0x3), pb, cbtx); + result = Device605_601_WriteDMA_TXP(ctx, qwA & ~0x3, be, 0, pbb, 4); + pb += cbtx; + cb -= cbtx; + qwA += cbtx; + } + // TX as 128-byte packets (aligned to 128-byte boundaries) + while(result && cb) { + cbtx = min(128 - (qwA & 0x7f), cb); + be = (cbtx & 0x3) ? (0xf >> (4 - (cbtx & 0x3))) : 0xf; + result = (cbtx <= 4) ? + Device605_601_WriteDMA_TXP(ctx, qwA, be, 0, pb, 4) : + Device605_601_WriteDMA_TXP(ctx, qwA, 0xf, be, pb, cbtx); + pb += cbtx; + cb -= cbtx; + qwA += cbtx; + } + return Device605_601_TxTlp(ctx, NULL, 0, FALSE, TRUE) && result; // Flush and Return. +} + +BOOL Device605_601_ListenTlp(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ DWORD dwTime) +{ + PDEVICE_CONTEXT_SP605_601 ctx = (PDEVICE_CONTEXT_SP605_601)ctxPcileech->hDevice; + QWORD tmStart = GetTickCount64(); + while(GetTickCount64() - tmStart < dwTime) { + Device605_601_TxTlp(ctx, NULL, 0, TRUE, TRUE); + Sleep(10); + Device605_601_RxTlpSynchronous(ctx); + } + return TRUE; +} + +BOOL Device605_601_WriteTlp(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ PBYTE pbTlp, _In_ DWORD cbTlp) +{ + PDEVICE_CONTEXT_SP605_601 ctx = (PDEVICE_CONTEXT_SP605_601)ctxPcileech->hDevice; + return Device605_601_TxTlp(ctx, pbTlp, cbTlp, FALSE, TRUE); +} + +BOOL Device605_601_Open(_Inout_ PPCILEECH_CONTEXT ctxPcileech) +{ + PDEVICE_CONTEXT_SP605_601 ctx; + ctx = LocalAlloc(LMEM_ZEROINIT, sizeof(DEVICE_CONTEXT_SP605_601)); + if(!ctx) { return FALSE; } + ctxPcileech->hDevice = (HANDLE)ctx; + Device601_601_InitializeFTDI(ctx); + if(!ctx->dev.hModule && ctxPcileech->cfg->fVerbose) { printf("Device Info: SP605 / FT601: Could not load FTD3XX.dll.\n"); } + if(!ctx->dev.hModule) { goto fail; } + if(!ctx->dev.hFTDI && ctxPcileech->cfg->fVerbose) { printf("Device Info: SP605 / FT601: Could not connect to device.\n"); } + if(!ctx->dev.hFTDI) { goto fail; } + Device605_601_GetDeviceID_FpgaVersion(ctx); + if(!ctx->wDeviceId) { goto fail; } + ctx->rxbuf.cbMax = (DWORD)(2.3 * SP601_601_MAX_SIZE_RX); // buffer size tuned to lowest possible (+margin) for performance. + ctx->rxbuf.pb = LocalAlloc(0, ctx->rxbuf.cbMax); + if(!ctx->rxbuf.pb) { goto fail; } + ctx->txbuf.cbMax = SP601_601_MAX_SIZE_TX + 0x10000; + ctx->txbuf.pb = LocalAlloc(0, ctx->txbuf.cbMax); + if(!ctx->txbuf.pb) { goto fail; } + ctx->isPrintTlp = ctxPcileech->cfg->fVerboseExtra; + // set callback functions and fix up config + ctxPcileech->cfg->dev.tp = PCILEECH_DEVICE_SP605_FT601; + ctxPcileech->cfg->dev.qwMaxSizeDmaIo = SP601_601_MAX_SIZE_RX; + ctxPcileech->cfg->dev.qwAddrMaxNative = 0x0000ffffffffffff; + ctxPcileech->cfg->dev.fPartialPageReadSupported = TRUE; + ctxPcileech->cfg->dev.pfnClose = Device605_601_Close; + ctxPcileech->cfg->dev.pfnProbeDMA = Device605_601_ProbeDMA; + ctxPcileech->cfg->dev.pfnReadDMA = Device605_601_ReadDMA; + ctxPcileech->cfg->dev.pfnWriteDMA = Device605_601_WriteDMA; + ctxPcileech->cfg->dev.pfnWriteTlp = Device605_601_WriteTlp; + ctxPcileech->cfg->dev.pfnListenTlp = Device605_601_ListenTlp; + // return + if(ctxPcileech->cfg->fVerbose) { printf("Device Info: SP605 / FT601.\n"); } + return TRUE; +fail: + Device605_601_Close(ctxPcileech); + return FALSE; +} + +#endif /* WIN32 */ +#if defined(LINUX) || defined(ANDROID) + +#include "device605_601.h" + +BOOL Device605_601_Open(_Inout_ PPCILEECH_CONTEXT ctx) +{ + if(ctx->cfg->dev.tp == PCILEECH_DEVICE_SP605_UART) { + printf("SP605 / FT601: Failed. Device currently only supported in PCILeech for Windows."); + } + return FALSE; +} + +#endif /* LINUX || ANDROID */ diff --git a/pcileech/device605_601.h b/pcileech/device605_601.h new file mode 100644 index 0000000..cf730be --- /dev/null +++ b/pcileech/device605_601.h @@ -0,0 +1,17 @@ +// device605_601.h : definitions related to the Xilinx SP605 dev board flashed with bitstream for FTDI UMFT601X-B addon-board. +// +// (c) Ulf Frisk, 2017 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __DEVICE605_601_H__ +#define __DEVICE605_601_H__ +#include "pcileech.h" + +/* +* Open a connection to the SP605/FT601 PCILeech flashed device. +* -- ctx +* -- result +*/ +BOOL Device605_601_Open(_Inout_ PPCILEECH_CONTEXT ctx); + +#endif /* __DEVICE605_601_H__ */ diff --git a/pcileech/device605.c b/pcileech/device605_uart.c similarity index 63% rename from pcileech/device605.c rename to pcileech/device605_uart.c index 2fb6c88..312d3e6 100644 --- a/pcileech/device605.c +++ b/pcileech/device605_uart.c @@ -1,11 +1,11 @@ -// device.c : implementation related to the Xilinx SP605 dev board flashed with @d_olex early access bitstream. (UART communication). +// device605_uart.c : implementation related to the Xilinx SP605 dev board flashed with @d_olex early access bitstream. (UART communication). // // (c) Ulf Frisk, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #ifdef WIN32 -#include "device605.h" +#include "device605_uart.h" #include "device.h" #include "tlp.h" @@ -26,12 +26,6 @@ #define ENDIAN_SWAP_DWORD(x) (x = (x << 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x >> 24)) -typedef struct tdDEVICE_CONTEXT_SP605_RXBUF { - DWORD cbMax; - DWORD cb; - PBYTE pb; -} DEVICE_CONTEXT_SP605_RXBUF, *PDEVICE_CONTEXT_SP605_RXBUF; - typedef struct tdDEVICE_CONTEXT_SP605 { HANDLE hCommCfg; HANDLE hCommPcie; @@ -43,17 +37,15 @@ typedef struct tdDEVICE_CONTEXT_SP605 { OVERLAPPED oRx; OVERLAPPED oCfg; HANDLE hRxBufferEvent; - PDEVICE_CONTEXT_SP605_RXBUF pRxBuffer; - VOID(*hRxTlpCallbackFn)(_Inout_ struct tdDEVICE_CONTEXT_SP605 *ctx605, _In_ PBYTE pb, _In_ DWORD cb); + PTLP_CALLBACK_BUF_MRd pMRdBuffer; + BOOL(*hRxTlpCallbackFn)(_Inout_ PTLP_CALLBACK_BUF_MRd pBufferMrd, _In_ PBYTE pb, _In_ DWORD cb, _In_opt_ HANDLE hEventCompleted); } DEVICE_CONTEXT_SP605, *PDEVICE_CONTEXT_SP605; -VOID Device605_RxTlp_Thread(PDEVICE_CONTEXT_SP605 ctx605); - //------------------------------------------------------------------------------- // SP605 implementation below. //------------------------------------------------------------------------------- -VOID Device605_Close(_Inout_ PPCILEECH_CONTEXT ctx) +VOID Device605_UART_Close(_Inout_ PPCILEECH_CONTEXT ctx) { PDEVICE_CONTEXT_SP605 ctx605 = (PDEVICE_CONTEXT_SP605)ctx->hDevice; if(!ctx605) { return; } @@ -63,7 +55,7 @@ VOID Device605_Close(_Inout_ PPCILEECH_CONTEXT ctx) } if(ctx605->hRxBufferEvent) { WaitForSingleObject(ctx605->hRxBufferEvent, INFINITE); - while(ctx605->pRxBuffer) { SwitchToThread(); } + while(ctx605->pMRdBuffer) { SwitchToThread(); } CloseHandle(ctx605->hRxBufferEvent); } if(ctx605->hCommCfg) { CloseHandle(ctx605->hCommCfg); } @@ -75,7 +67,7 @@ VOID Device605_Close(_Inout_ PPCILEECH_CONTEXT ctx) ctx->hDevice = 0; } -HANDLE Device605_Open_COM(_In_ LPSTR szCOM) +HANDLE Device605_UART_Open_COM(_In_ LPSTR szCOM) { DCB dcb = { 0 }; HANDLE hComm; @@ -90,7 +82,7 @@ HANDLE Device605_Open_COM(_In_ LPSTR szCOM) return hComm; } -WORD Device605_GetDeviceID(_In_ PDEVICE_CONTEXT_SP605 ctx605) +WORD Device605_UART_GetDeviceID(_In_ PDEVICE_CONTEXT_SP605 ctx605) { DWORD dw, txrx[] = { 0x00000000, 0x00000000 }; if(!WriteFile(ctx605->hCommCfg, txrx, sizeof(txrx), &dw, &ctx605->oCfg)) { @@ -100,52 +92,12 @@ WORD Device605_GetDeviceID(_In_ PDEVICE_CONTEXT_SP605 ctx605) if(!ReadFile(ctx605->hCommCfg, txrx, sizeof(txrx), &dw, &ctx605->oCfg)) { if(ERROR_IO_PENDING != GetLastError()) { return 0; } if(WAIT_TIMEOUT == WaitForSingleObject(ctx605->oCfg.hEvent, SP605_COM_TIMEOUT)) { return 0; } - if(!GetOverlappedResult(ctx605->hCommPcie, &ctx605->oCfg, &dw, FALSE)) { return 0; } + if(!GetOverlappedResult(ctx605->hCommCfg, &ctx605->oCfg, &dw, FALSE)) { return 0; } } return (WORD)_byteswap_ulong(txrx[0]); } -BOOL Device605_Open(_Inout_ PPCILEECH_CONTEXT ctx) -{ - DWORD i; - CHAR szCOM[] = { 'C', 'O', 'M', 'x', 0 }; - PDEVICE_CONTEXT_SP605 ctx605; - ctx605 = LocalAlloc(LMEM_ZEROINIT, sizeof(DEVICE_CONTEXT_SP605)); - if(!ctx605) { return FALSE; } - ctx->hDevice = (HANDLE)ctx605; - // open COM ports - for(i = 1; i <= 9; i++) { - szCOM[3] = (CHAR)('0' + i); - if(!ctx605->hCommPcie) { - ctx605->hCommPcie = Device605_Open_COM(szCOM); - } else { - ctx605->hCommCfg = Device605_Open_COM(szCOM); - if(ctx605->hCommCfg) { break; } - } - } - if(!ctx605->hCommPcie || !ctx605->hCommCfg) { goto fail; } - SetupComm(ctx605->hCommPcie, 0x8000, 0x8000); - ctx605->oTx.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!ctx605->oTx.hEvent) { goto fail; } - ctx605->oRx.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!ctx605->oRx.hEvent) { goto fail; } - ctx605->oCfg.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!ctx605->oCfg.hEvent) { goto fail; } - ctx605->hRxBufferEvent = CreateEvent(NULL, TRUE, TRUE, NULL); - if(!ctx605->hRxBufferEvent) { goto fail; } - ctx605->wDeviceId = Device605_GetDeviceID(ctx605); - if(!ctx605->wDeviceId) { goto fail; } - ctx605->isPrintTlp = ctx->cfg->fVerboseExtra; - ctx605->hThreadRx = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Device605_RxTlp_Thread, ctx605, 0, NULL); // start rx thread, must be last in open - if(!ctx605->hThreadRx) { goto fail; } - if(ctx->cfg->fVerbose) { printf("Device Info: SP605.\n"); } - return TRUE; -fail: - Device605_Close(ctx); - return FALSE; -} - -BOOL Device605_TxTlp(_In_ PDEVICE_CONTEXT_SP605 ctx605, _In_ PBYTE pbTlp, _In_ DWORD cbTlp) +BOOL Device605_UART_TxTlp(_In_ PDEVICE_CONTEXT_SP605 ctx605, _In_ PBYTE pbTlp, _In_ DWORD cbTlp) { DWORD pdwTx[1024], cTx, i, dwTxed; if(!cbTlp) { return TRUE; } @@ -166,30 +118,7 @@ BOOL Device605_TxTlp(_In_ PDEVICE_CONTEXT_SP605 ctx605, _In_ PBYTE pbTlp, _In_ D (GetLastError() == ERROR_IO_PENDING && GetOverlappedResult(ctx605->hCommPcie, &ctx605->oTx, &dwTxed, TRUE)); } -VOID Device605_RxTlp_CallbackMRd(_Inout_ PDEVICE_CONTEXT_SP605 ctx605, _In_ PBYTE pb, _In_ DWORD cb) -{ - PDEVICE_CONTEXT_SP605_RXBUF prxbuf = ctx605->pRxBuffer; - PTLP_HDR_CplD hdrC = (PTLP_HDR_CplD)pb; - PTLP_HDR hdr = (PTLP_HDR)pb; - PDWORD buf = (PDWORD)pb; - DWORD o, c; - buf[0] = _byteswap_ulong(buf[0]); - if(cb < ((DWORD)hdr->Length << 2) - 12) { return; } - if((hdr->TypeFmt == TLP_CplD) && prxbuf) { - buf[1] = _byteswap_ulong(buf[1]); - buf[2] = _byteswap_ulong(buf[2]); - // NB! read algorithm below only support reading full 4kB pages _or_ - // partial page if starting at page boundry and read is less than 4kB. - o = (hdrC->Tag << 12) + min(0x1000, prxbuf->cbMax) - (hdrC->ByteCount ? hdrC->ByteCount : 0x1000); - c = (DWORD)hdr->Length << 2; - memcpy(prxbuf->pb + o, pb + 12, c); - if(prxbuf->cbMax <= (DWORD)InterlockedAdd(&prxbuf->cb, c)) { - SetEvent(ctx605->hRxBufferEvent); - } - } -} - -VOID Device605_RxTlp_Thread(_In_ PDEVICE_CONTEXT_SP605 ctx605) +VOID Device605_UART_RxTlp_Thread(_In_ PDEVICE_CONTEXT_SP605 ctx605) { DWORD rx[2], dwTlp[1024], cbRead, dwResult, cdwTlp = 0; while(!ctx605->isTerminateThreadRx) { @@ -213,8 +142,8 @@ VOID Device605_RxTlp_Thread(_In_ PDEVICE_CONTEXT_SP605 ctx605) if(ctx605->isPrintTlp) { TLP_Print((PBYTE)dwTlp, cdwTlp << 2, FALSE); } - if(ctx605->hRxTlpCallbackFn) { - ctx605->hRxTlpCallbackFn(ctx605, (PBYTE)dwTlp, cdwTlp << 2); + if(ctx605->hRxTlpCallbackFn && ctx605->pMRdBuffer) { + ctx605->hRxTlpCallbackFn(ctx605->pMRdBuffer, (PBYTE)dwTlp, cdwTlp << 2, ctx605->hRxBufferEvent); } } cdwTlp = 0; @@ -225,10 +154,10 @@ VOID Device605_RxTlp_Thread(_In_ PDEVICE_CONTEXT_SP605 ctx605) ctx605->isTerminateThreadRx = TRUE; } -BOOL Device605_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb) +BOOL Device605_UART_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb) { PDEVICE_CONTEXT_SP605 ctx605 = (PDEVICE_CONTEXT_SP605)ctx->hDevice; - DEVICE_CONTEXT_SP605_RXBUF rxbuf; + TLP_CALLBACK_BUF_MRd rxbuf; DWORD tx[4], o, i; BOOL is32; PTLP_HDR_MRdWr64 hdrRd64 = (PTLP_HDR_MRdWr64)tx; @@ -241,9 +170,9 @@ BOOL Device605_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ P rxbuf.cb = 0; rxbuf.pb = pb; rxbuf.cbMax = cb; - ctx605->pRxBuffer = &rxbuf; + ctx605->pMRdBuffer = &rxbuf; ResetEvent(ctx605->hRxBufferEvent); - ctx605->hRxTlpCallbackFn = Device605_RxTlp_CallbackMRd; + ctx605->hRxTlpCallbackFn = TLP_CallbackMRd; // transmit TLPs for(o = 0; o < cb; o += 0x1000) { memset(tx, 0, 16); @@ -258,7 +187,7 @@ BOOL Device605_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ P hdrRd32->Address = (DWORD)(qwAddr + o); } else { hdrRd64->h.TypeFmt = TLP_MRd64; - hdrRd32->h.Length = (WORD)((cb < 0x1000) ? cb >> 2 : 0); + hdrRd64->h.Length = (WORD)((cb < 0x1000) ? cb >> 2 : 0); hdrRd64->RequesterID = ctx605->wDeviceId; hdrRd64->Tag = (BYTE)(o >> 12); hdrRd64->FirstBE = 0xf; @@ -269,48 +198,28 @@ BOOL Device605_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ P for(i = 0; i < 4; i++) { ENDIAN_SWAP_DWORD(tx[i]); } - Device605_TxTlp(ctx605, (PBYTE)tx, is32 ? 12 : 16); + Device605_UART_TxTlp(ctx605, (PBYTE)tx, is32 ? 12 : 16); } // wait for result WaitForSingleObject(ctx605->hRxBufferEvent, SP605_READ_TIMEOUT); ctx605->hRxTlpCallbackFn = NULL; - ctx605->pRxBuffer = NULL; + ctx605->pMRdBuffer = NULL; SetEvent(ctx605->hRxBufferEvent); return rxbuf.cb >= rxbuf.cbMax; } -VOID Device605_RxTlp_CallbackProbeDMA(_Inout_ PDEVICE_CONTEXT_SP605 ctx605, _In_ PBYTE pb, _In_ DWORD cb) -{ - PDEVICE_CONTEXT_SP605_RXBUF prxbuf = ctx605->pRxBuffer; - PTLP_HDR_CplD hdrC = (PTLP_HDR_CplD)pb; - PDWORD buf = (PDWORD)pb; - DWORD i; - if(cb < 16) { return; } // min size CplD = 16 bytes. - buf[0] = _byteswap_ulong(buf[0]); - buf[1] = _byteswap_ulong(buf[1]); - buf[2] = _byteswap_ulong(buf[2]); - if((hdrC->h.TypeFmt == TLP_CplD) && prxbuf) { - // 5 low address bits coded into the dword read, 5 high address bits coded into tag. - i = ((hdrC->Tag & 0x1f) << 5) + ((hdrC->LowerAddress >> 2) & 0x1f); - prxbuf->pb[i] = 1; - if(prxbuf->cbMax <= (DWORD)InterlockedAdd(&prxbuf->cb, 1)) { - SetEvent(ctx605->hRxBufferEvent); - } - } -} - -VOID Device605_ProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap) +VOID Device605_UART_ProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap) { DWORD i, j; PDEVICE_CONTEXT_SP605 ctx605 = (PDEVICE_CONTEXT_SP605)ctx->hDevice; - DEVICE_CONTEXT_SP605_RXBUF rxbuf; + TLP_CALLBACK_BUF_MRd rxbuf; DWORD tx[4]; BOOL is32; PTLP_HDR_MRdWr64 hdrRd64 = (PTLP_HDR_MRdWr64)tx; PTLP_HDR_MRdWr32 hdrRd32 = (PTLP_HDR_MRdWr32)tx; // split probe into processing chunks if too large... while(cPages > SP605_PROBE_MAXPAGES) { - Device605_ProbeDMA(ctx, qwAddr, SP605_PROBE_MAXPAGES, pbResultMap); + Device605_UART_ProbeDMA(ctx, qwAddr, SP605_PROBE_MAXPAGES, pbResultMap); cPages -= SP605_PROBE_MAXPAGES; pbResultMap += SP605_PROBE_MAXPAGES; qwAddr += SP605_PROBE_MAXPAGES << 12; @@ -320,9 +229,9 @@ VOID Device605_ProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ D rxbuf.cb = 0; rxbuf.pb = pbResultMap; rxbuf.cbMax = cPages; - ctx605->pRxBuffer = &rxbuf; + ctx605->pMRdBuffer = &rxbuf; ResetEvent(ctx605->hRxBufferEvent); - ctx605->hRxTlpCallbackFn = Device605_RxTlp_CallbackProbeDMA; + ctx605->hRxTlpCallbackFn = TLP_CallbackMRdProbe; // transmit TLPs for(i = 0; i < cPages; i++) { memset(tx, 0, 16); @@ -337,27 +246,27 @@ VOID Device605_ProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ D hdrRd32->Tag = (BYTE)((i >> 5) & 0x1f); // 5 high address bits coded into tag. } else { hdrRd64->h.TypeFmt = TLP_MRd64; - hdrRd32->h.Length = 1; - hdrRd32->RequesterID = ctx605->wDeviceId; - hdrRd32->FirstBE = 0xf; - hdrRd32->LastBE = 0; + hdrRd64->h.Length = 1; + hdrRd64->RequesterID = ctx605->wDeviceId; + hdrRd64->FirstBE = 0xf; + hdrRd64->LastBE = 0; hdrRd64->AddressHigh = (DWORD)((qwAddr + (i << 12)) >> 32); - hdrRd32->Address = (DWORD)(qwAddr + (i << 12) + ((i & 0x1f) << 2)); // 5 low address bits coded into the dword read. - hdrRd32->Tag = (BYTE)((i >> 5) & 0x1f); // 5 high address bits coded into tag. + hdrRd64->AddressLow = (DWORD)(qwAddr + (i << 12) + ((i & 0x1f) << 2)); // 5 low address bits coded into the dword read. + hdrRd64->Tag = (BYTE)((i >> 5) & 0x1f); // 5 high address bits coded into tag. } for(j = 0; j < 4; j++) { ENDIAN_SWAP_DWORD(tx[j]); } - Device605_TxTlp(ctx605, (PBYTE)tx, is32 ? 12 : 16); + Device605_UART_TxTlp(ctx605, (PBYTE)tx, is32 ? 12 : 16); } // wait for result WaitForSingleObject(ctx605->hRxBufferEvent, SP605_PROBE_TIMEOUT); ctx605->hRxTlpCallbackFn = NULL; - ctx605->pRxBuffer = NULL; + ctx605->pMRdBuffer = NULL; SetEvent(ctx605->hRxBufferEvent); } -BOOL Device605_WriteDMA_TXP(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwA, _In_ BYTE bFirstBE, _In_ BYTE bLastBE, _In_ PBYTE pb, _In_ DWORD cb) +BOOL Device605_UART_WriteDMA_TXP(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwA, _In_ BYTE bFirstBE, _In_ BYTE bLastBE, _In_ PBYTE pb, _In_ DWORD cb) { PDEVICE_CONTEXT_SP605 ctx605 = (PDEVICE_CONTEXT_SP605)ctx->hDevice; DWORD txbuf[36], i, cbTlp; @@ -391,10 +300,10 @@ BOOL Device605_WriteDMA_TXP(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwA, _In_ memcpy(pbTlp + 16, pb, cb); cbTlp = (16 + cb + 3) & ~0x3; } - return Device605_TxTlp(ctx605, pbTlp, cbTlp); + return Device605_UART_TxTlp(ctx605, pbTlp, cbTlp); } -BOOL Device605_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwA, _In_ PBYTE pb, _In_ DWORD cb) +BOOL Device605_UART_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwA, _In_ PBYTE pb, _In_ DWORD cb) { BOOL result = TRUE; BYTE be, pbb[4]; @@ -406,7 +315,7 @@ BOOL Device605_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwA, _In_ PBYT be <<= qwA & 0x3; cbtx = min(cb, 4 - (qwA & 0x3)); memcpy(pbb + (qwA & 0x3), pb, cbtx); - result = Device605_WriteDMA_TXP(ctx, qwA & ~0x3, be, 0, pbb, 4); + result = Device605_UART_WriteDMA_TXP(ctx, qwA & ~0x3, be, 0, pbb, 4); pb += cbtx; cb -= cbtx; qwA += cbtx; @@ -416,8 +325,8 @@ BOOL Device605_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwA, _In_ PBYT cbtx = min(128 - (qwA & 0x7f), cb); be = (cbtx & 0x3) ? (0xf >> (4 - (cbtx & 0x3))) : 0xf; result = (cbtx <= 4) ? - Device605_WriteDMA_TXP(ctx, qwA, be, 0, pb, 4) : - Device605_WriteDMA_TXP(ctx, qwA, 0xf, be, pb, cbtx); + Device605_UART_WriteDMA_TXP(ctx, qwA, be, 0, pb, 4) : + Device605_UART_WriteDMA_TXP(ctx, qwA, 0xf, be, pb, cbtx); pb += cbtx; cb -= cbtx; qwA += cbtx; @@ -425,57 +334,80 @@ BOOL Device605_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwA, _In_ PBYT return result; } -VOID Action_Device605_TlpTx(_Inout_ PPCILEECH_CONTEXT ctx) +BOOL Device605_UART_ListenTlp(_Inout_ PPCILEECH_CONTEXT ctx, _In_ DWORD dwTime) { - if(ctx->cfg->tpDevice != PCILEECH_DEVICE_SP605) { - printf("TLP: Failed. unsupported device.\n"); - return; - } - if(Device605_TxTlp((PDEVICE_CONTEXT_SP605)ctx->hDevice, ctx->cfg->pbIn, (DWORD)ctx->cfg->cbIn)) { - printf("TLP: Success.\n"); - // If no custom exit timeout is set wait 500ms to receive any TLP responses. - if(ctx->cfg->qwWaitBeforeExit == 0) { - Sleep(500); - } - } else { - printf("TLP: Failed. TX error.\n"); - } + Sleep(dwTime); + return TRUE; } -#endif /* WIN32 */ -#if defined(LINUX) || defined(ANDROID) - -#include "device605.h" - -BOOL Device605_Open(_Inout_ PPCILEECH_CONTEXT ctx) +BOOL Device605_UART_WriteTlp(_Inout_ PPCILEECH_CONTEXT ctx, _In_ PBYTE pbTlp, _In_ DWORD cbTlp) { - printf("SP605: Failed. Device only supported in PCILeech for Windows."); - return FALSE; + return Device605_UART_TxTlp((PDEVICE_CONTEXT_SP605)ctx->hDevice, pbTlp, cbTlp); } -VOID Device605_Close(_Inout_ PPCILEECH_CONTEXT ctx) +BOOL Device605_UART_Open(_Inout_ PPCILEECH_CONTEXT ctx) { - return; + DWORD i; + CHAR szCOM[] = { 'C', 'O', 'M', 'x', 0 }; + PDEVICE_CONTEXT_SP605 ctx605; + ctx605 = LocalAlloc(LMEM_ZEROINIT, sizeof(DEVICE_CONTEXT_SP605)); + if(!ctx605) { return FALSE; } + ctx->hDevice = (HANDLE)ctx605; + // open COM ports + for(i = 1; i <= 9; i++) { + szCOM[3] = (CHAR)('0' + i); + if(!ctx605->hCommPcie) { + ctx605->hCommPcie = Device605_UART_Open_COM(szCOM); + } else { + ctx605->hCommCfg = Device605_UART_Open_COM(szCOM); + if(ctx605->hCommCfg) { break; } + } + } + if(!ctx605->hCommPcie || !ctx605->hCommCfg) { goto fail; } + SetupComm(ctx605->hCommPcie, 0x8000, 0x8000); + ctx605->oTx.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!ctx605->oTx.hEvent) { goto fail; } + ctx605->oRx.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!ctx605->oRx.hEvent) { goto fail; } + ctx605->oCfg.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!ctx605->oCfg.hEvent) { goto fail; } + ctx605->hRxBufferEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if(!ctx605->hRxBufferEvent) { goto fail; } + ctx605->wDeviceId = Device605_UART_GetDeviceID(ctx605); + if(!ctx605->wDeviceId) { goto fail; } + ctx605->isPrintTlp = ctx->cfg->fVerboseExtra; + ctx605->hThreadRx = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Device605_UART_RxTlp_Thread, ctx605, 0, NULL); // start rx thread, must be last in open + if(!ctx605->hThreadRx) { goto fail; } + // set callback functions and fix up config + ctx->cfg->dev.tp = PCILEECH_DEVICE_SP605_UART; + ctx->cfg->dev.qwMaxSizeDmaIo = 0x4000; + ctx->cfg->dev.qwAddrMaxNative = 0x0000ffffffffffff; + ctx->cfg->dev.fPartialPageReadSupported = TRUE; + ctx->cfg->dev.pfnClose = Device605_UART_Close; + ctx->cfg->dev.pfnProbeDMA = Device605_UART_ProbeDMA; + ctx->cfg->dev.pfnReadDMA = Device605_UART_ReadDMA; + ctx->cfg->dev.pfnWriteDMA = Device605_UART_WriteDMA; + ctx->cfg->dev.pfnWriteTlp = Device605_UART_WriteTlp; + ctx->cfg->dev.pfnListenTlp = Device605_UART_ListenTlp; + // return + if(ctx->cfg->fVerbose) { printf("Device Info: SP605 / UART.\n"); } + return TRUE; +fail: + Device605_UART_Close(ctx); + return FALSE; } -VOID Action_Device605_TlpTx(_Inout_ PPCILEECH_CONTEXT ctx) -{ - printf("TLP: Failed. Operation only supported in PCILeech for Windows."); -} +#endif /* WIN32 */ +#if defined(LINUX) || defined(ANDROID) -BOOL Device605_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb) -{ - return FALSE; -} +#include "device605_uart.h" -BOOL Device605_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb) +BOOL Device605_UART_Open(_Inout_ PPCILEECH_CONTEXT ctx) { + if(ctx->cfg->dev.tp == PCILEECH_DEVICE_SP605_UART) { + printf("SP605 / UART: Failed. Device currently only supported in PCILeech for Windows."); + } return FALSE; } -VOID Device605_ProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap) -{ - ; -} - #endif /* LINUX || ANDROID */ diff --git a/pcileech/device605_uart.h b/pcileech/device605_uart.h new file mode 100644 index 0000000..9496bc7 --- /dev/null +++ b/pcileech/device605_uart.h @@ -0,0 +1,17 @@ +// device605_uart.h : definitions related to the Xilinx SP605 dev board flashed with @d_olex early access bitstream. (UART communication). +// +// (c) Ulf Frisk, 2017 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __DEVICE605_UART_H__ +#define __DEVICE605_UART_H__ +#include "pcileech.h" + +/* +* Open a connection to the SP605/UART PCILeech flashed device. +* -- ctx +* -- result +*/ +BOOL Device605_UART_Open(_Inout_ PPCILEECH_CONTEXT ctx); + +#endif /* __DEVICE605_UART_H__ */ diff --git a/pcileech/executor.c b/pcileech/executor.c index 955cb2e..7713f40 100644 --- a/pcileech/executor.c +++ b/pcileech/executor.c @@ -161,7 +161,7 @@ VOID Exec_Callback(_Inout_ PPCILEECH_CONTEXT ctx, _Inout_ PHANDLE phCallback) if(ph->is.bin.seqAck >= ph->os.bin.seq) { return; } cbLength = 0; result = - DeviceReadDMA(ctx, ctx->pk->DMAAddrPhysical + ctx->pk->dataOutExtraOffset, ph->pbDMA, (DWORD)SIZE_PAGE_ALIGN_4K(ctx->pk->dataOutExtraLength), 0) && + DeviceReadDMAEx(ctx, ctx->pk->DMAAddrPhysical + ctx->pk->dataOutExtraOffset, ph->pbDMA, (DWORD)SIZE_PAGE_ALIGN_4K(ctx->pk->dataOutExtraLength), NULL) && (cbLength = fwrite(ph->pbDMA, 1, ctx->pk->dataOutExtraLength, ph->pFileOutput)) && (ctx->pk->dataOutExtraLength == cbLength); ph->qwFileWritten += cbLength; @@ -237,7 +237,7 @@ BOOL Exec_ExecSilent(_Inout_ PPCILEECH_CONTEXT ctx, _In_ LPSTR szShellcodeName, *pcbOut = pk->dataOutExtraLength; *ppbOut = (PBYTE)LocalAlloc(0, SIZE_PAGE_ALIGN_4K(*pcbOut)); if(!*ppbOut) { result = FALSE; goto fail; } - result = DeviceReadDMA(ctx, pk->DMAAddrPhysical + pk->dataOutExtraOffset, *ppbOut, SIZE_PAGE_ALIGN_4K(*pcbOut), 0); + result = SIZE_PAGE_ALIGN_4K(*pcbOut) == DeviceReadDMAEx(ctx, pk->DMAAddrPhysical + pk->dataOutExtraOffset, *ppbOut, SIZE_PAGE_ALIGN_4K(*pcbOut), NULL); } fail: LocalFree(pKmdExec); @@ -337,7 +337,7 @@ VOID ActionExecShellcode(_Inout_ PPCILEECH_CONTEXT ctx) if(cbLength > 0) { // read extra output buffer if(!(pbBuffer = LocalAlloc(LMEM_ZEROINIT, SIZE_PAGE_ALIGN_4K(cbLength))) || - !DeviceReadDMA(ctx, pk->DMAAddrPhysical + pk->dataOutExtraOffset, pbBuffer, SIZE_PAGE_ALIGN_4K(cbLength), 0)) { + !DeviceReadDMAEx(ctx, pk->DMAAddrPhysical + pk->dataOutExtraOffset, pbBuffer, SIZE_PAGE_ALIGN_4K(cbLength), NULL)) { printf("EXEC: Error reading output.\n"); goto fail; } diff --git a/pcileech/extra.c b/pcileech/extra.c index 45b43b2..9014fc1 100644 --- a/pcileech/extra.c +++ b/pcileech/extra.c @@ -16,7 +16,7 @@ VOID Extra_MacFVRecover_ReadMemory_Optimized(_Inout_ PPCILEECH_CONTEXT ctx, _Ino 0x88000000, 0x89000000, 0x8a000000, 0x8b000000, 0x8c000000, 0x8d000000, 0x8e000000, 0x8f000000 }; for(i = 0; i < sizeof(dwOffsets) / sizeof(DWORD); i++) { - DeviceReadDMA(ctx, dwOffsets[i], pb512M + dwOffsets[i] - 0x70000000, 0x01000000, PCILEECH_MEM_FLAG_RETRYONFAIL); + DeviceReadDMAEx(ctx, dwOffsets[i], pb512M + dwOffsets[i] - 0x70000000, 0x01000000, NULL); } } @@ -156,7 +156,7 @@ VOID Action_MacDisableVtd(_Inout_ PPCILEECH_CONTEXT ctx) // DMAR table assumed to be on page boundary. This doesn't have to be true, // but it seems like it is on the MACs. for(i = 0; i < sizeof(dwOffsets) / sizeof(DWORD); i++) { - if(DeviceReadDMA(ctx, dwOffsets[i], pb16M, 0x01000000, 0)) { + if(DeviceReadDMAEx(ctx, dwOffsets[i], pb16M, 0x01000000, NULL)) { for(j = 0; j < 0x01000000; j += 0x1000) { if(*(PQWORD)(pb16M + j) == 0x0000008852414d44) { dwAddress = dwOffsets[i] + j; @@ -193,3 +193,13 @@ VOID Action_PT_Phys2Virt(_Inout_ PPCILEECH_CONTEXT ctx) printf("PT_PHYS2VIRT: Failed.\n"); } } + +VOID Action_TlpTx(_Inout_ PPCILEECH_CONTEXT ctx) +{ + if(ctx->cfg->cbIn < 12) { + printf("Action_TlpTx: Invalid TLP (too short).\n"); + } + printf("TLP: Transmitting PCIe TLP.%s\n", ctx->cfg->fVerboseExtra ? "" : " (use -vv option for detailed info)."); + DeviceWriteTlp(ctx, ctx->cfg->pbIn, (DWORD)ctx->cfg->cbIn); + DeviceListenTlp(ctx, 100); +} diff --git a/pcileech/extra.h b/pcileech/extra.h index f1e2f92..b5b319c 100644 --- a/pcileech/extra.h +++ b/pcileech/extra.h @@ -33,4 +33,10 @@ VOID Action_MacDisableVtd(_Inout_ PPCILEECH_CONTEXT ctx); */ VOID Action_PT_Phys2Virt(_Inout_ PPCILEECH_CONTEXT ctx); +/* +* Transmit the TLP data specified in the -in parameter. +* -- ctx +*/ +VOID Action_TlpTx(_Inout_ PPCILEECH_CONTEXT ctx); + #endif /* __EXTRA_H__ */ diff --git a/pcileech/help.c b/pcileech/help.c index 0e9e68b..8619204 100644 --- a/pcileech/help.c +++ b/pcileech/help.c @@ -32,8 +32,9 @@ VOID Help_ShowGeneral() " erted KMD. The already inserted KMD will be left intact upon exit. If the KMD\n" \ " contains a kernel mode signature the kernel module will be loaded and then un-\n" \ " loaded on program exit ( except for the kmdload command ). \n" \ - " KMD mode may access all memory. DMA mode may only access memory below 4GB if\n" \ - " USB3380 hardware is used. Commands marked 'W' are only available on Windows.\n" \ + " KMD mode may access all memory (available to the kernel of the target system).\n" \ + " DMA mode may only access lower 4GB of memory if USB3380 hardware is used. \n" \ + " DMA mode may access all memory if FPGA based hardware such as SP605 is used. \n" \ " For detailed help about a specific command type: pcileech.exe -help\n" \ " General syntax: pcileech.exe [- ] ... \n" \ " Valid commands and valid MODEs [ and options ] \n" \ @@ -45,17 +46,17 @@ VOID Help_ShowGeneral() " [implant] KMD [ in, out, s, 0..9 ] \n" \ " kmdload DMA [ pt, cr3 ] \n" \ " kmdexit KMD \n" \ - " mount W KMD [ s ] \n" \ + " mount KMD [ s ] (Windows only feature) \n" \ " pagedisplay DMA,KMD [ min ] \n" \ " pt_phys2virt DMA,KMD [ cr3, 0 ] \n" \ " testmemread DMA [ min ] \n" \ " testmemreadwrite DMA [ min ] \n" \ " Device specific commands and valid MODEs [ and options ] (and device): \n" \ - " usb3380_flash DMA,KMD [ in ] (USB3380) \n" \ - " usb3380_8051start DMA,KMD [ in ] (USB3380) \n" \ - " usb3380_8051stop DMA,KMD (USB3380) \n" \ - " tlp W DMA [ in ] (SP605) \n" \ - " probe W DMA [ in ] (SP605) \n" \ + " usb3380_flash DMA,KMD [ in ] (USB3380) \n" \ + " usb3380_8051start DMA,KMD [ in ] (USB3380) \n" \ + " usb3380_8051stop DMA,KMD (USB3380) \n" \ + " tlp DMA [ in ] (FPGA) \n" \ + " probe DMA [ in ] (FPGA) \n" \ " System specific commands and valid MODEs [ and options ]: \n" \ " mac_fvrecover DMA \n" \ " mac_fvrecover2 DMA \n" \ @@ -72,6 +73,7 @@ VOID Help_ShowGeneral() " -all : search all memory for signature - do not stop at first occurrence. \n" \ " Option has no value. Example: -all \n" \ " -vv : extra verbose option. Same as -v but even more detailed output. \n" \ + " Option shows raw PCIe TLPs received/sent if FPGA is used. \n" \ " -v : verbose option. Additional information is displayed in the output. \n" \ " The memory map is shown when searching/dumping memory as an example. \n" \ " Affects all modes and commands. \n" \ @@ -85,11 +87,11 @@ VOID Help_ShowGeneral() " Option has no value. Example: -usb2 \n" \ " -iosize: max DMA i/o size. Hardware DMA requests larger than iosize will \n" \ " be discarded. Affects all modes and commands. \n" \ - " -wait: wait in seconds before exit. Useful when viewing received PCIe TLPs. \n" \ + " -tlpwait: Wait in seconds while listening for PCIe TLPs. \n" \ + " Wait occurs after any other actions have been completed. \n" \ + " -device: force the use of a specific hardware device instead of auto-detect.\n" \ " Affects all modes and commands. \n" \ - " -device: specify a hardware device other than the USB3380 to use. \n" \ - " Affects all modes and commands. \n" \ - " Valid options: USB3380, SP605 \n" \ + " Valid options: USB3380, SP605_UART, SP605_FT601 \n" \ " -help: show help about the selected command or implant and then exit \n" \ " without running the command. Affects all modes and commands. \n" \ " Option has no value. Example: -help \n" \ @@ -136,7 +138,7 @@ VOID Help_ShowInfo() printf( " PCILEECH INFORMATION \n" \ " PCILeech (c) 2016, 2017 Ulf Frisk \n" \ - " Version: 2.2 \n" \ + " Version: 2.3 \n" \ " License: GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 \n" \ " Contact information: pcileech@frizk.net \n" \ " System requirements: 64-bit Windows 7, 10 or Linux. \n" \ @@ -145,9 +147,11 @@ VOID Help_ShowInfo() " Slotscreamer - https://github.com/NSAPlayset/SLOTSCREAMER \n" \ " Inception - https://github.com/carmaa/inception \n" \ " Google USB driver - https://developer.android.com/sdk/win-usb.html#download \n" \ + " FTDI FT601: - http://www.ftdichip.com/Drivers/D3XX.htm \n" \ " Dokany - https://github.com/dokan-dev/dokany/releases/latest \n" \ " ---------------- \n" \ " Use with USB3380 hardware programmed as a PCILeech device. \n" \ + " Use with SP605/FT601 harware programmed as a PCILeech FPGA device. \n" \ " Use with SP605 hardware / 'PCI Express DIY hacking toolkit' by cr4sh/@d_olex. \n\n" \ " ---------------- \n" \ " Driver information (USB3380/Windows): \n" \ @@ -155,6 +159,12 @@ VOID Help_ShowInfo() " device masks as a Google Glass. Please download and install the Google USB \n" \ " driver before proceeding by using the USB3380 device. USB3 is recommended \n" \ " to performance reasons (USB2 will work but impact performance). \n" \ + " Driver information (SP605/FT601/Windows): \n" \ + " The PCILeech programmed SP605 FPGA development board with the FT601 USB3 \n" \ + " addon board requires drivers for Windows. The drivers are on Windows update \n" \ + " and is installed automatically at first use. PCILeech also requires the FTDI\n" \ + " application library (DLL). Download this library from the FTDI web site and \n" \ + " place the 64-bit FTD3XX.dll alongside pcileech.exe. \n" \ " Driver information (Dokany/Windows): \n" \ " To be able to use the 'mount' functionality for filesystem browsing and live\n" \ " memory file access PCILeech requires Dokany to be installed for virtual file\n" \ diff --git a/pcileech/kmd.c b/pcileech/kmd.c index 4917f68..7123c3c 100644 --- a/pcileech/kmd.c +++ b/pcileech/kmd.c @@ -92,12 +92,11 @@ BOOL KMD_FindSignature1(_Inout_ PPCILEECH_CONTEXT ctx, _Inout_ PSIGNATURE pSigna return FALSE; } // loop kmd-find - qwAddrMax = min(ctx->cfg->qwAddrMax, ((ctx->cfg->tpDevice == PCILEECH_DEVICE_USB3380) ? 0xffffffffUL : 0x0000ffffffffffffULL)); + qwAddrMax = min(ctx->cfg->qwAddrMax, ctx->cfg->dev.qwAddrMaxNative); PageStatInitialize(&pageStat, qwAddrCurrent, qwAddrMax, "Searching for KMD location", FALSE, FALSE); while(qwAddrCurrent < qwAddrMax) { pageStat.qwAddr = qwAddrCurrent; - if(DeviceReadDMA(ctx, qwAddrCurrent, pbBuffer8M, 0x800000, 0)) { - pageStat.cPageSuccess += 2048; + if(DeviceReadDMAEx(ctx, qwAddrCurrent, pbBuffer8M, 0x800000, &pageStat)) { result = KMD_FindSignature2(pbBuffer8M, 2048, qwAddrCurrent, pSignatures, cSignatures, pdwSignatureMatchIdx); if(result) { LocalFree(pbBuffer8M); @@ -105,8 +104,6 @@ BOOL KMD_FindSignature1(_Inout_ PPCILEECH_CONTEXT ctx, _Inout_ PSIGNATURE pSigna PageStatClose(&pageStat); return TRUE; } - } else { - pageStat.cPageFail += 2048; } qwAddrCurrent += 0x800000; } @@ -210,7 +207,7 @@ BOOL KMD_MacOSKernelSeekSignature(_Inout_ PPCILEECH_CONTEXT ctx, _Out_ PSIGNATUR cbTextHIB = (cbTextHIB + 0xfff) & 0xfffff000; pbTextHIB = LocalAlloc(0, cbTextHIB); if(!pbTextHIB) { return FALSE; } - if(!DeviceReadDMA(ctx, dwTextHIB, pbTextHIB, cbTextHIB, PCILEECH_MEM_FLAG_RETRYONFAIL)) { + if(!DeviceReadDMAEx(ctx, dwTextHIB, pbTextHIB, cbTextHIB, NULL)) { LocalFree(pbTextHIB); return FALSE; } @@ -235,7 +232,7 @@ BOOL KMD_FreeBSDKernelSeekSignature(_Inout_ PPCILEECH_CONTEXT ctx, _Out_ PSIGNAT PBYTE pb64M = LocalAlloc(LMEM_ZEROINIT, 0x04000000); if(!pb64M) { return FALSE; } for(i = 0x01000000; i < 0x04000000; i += 0x01000000) { - DeviceReadDMA(ctx, i, pb64M + i, 0x01000000, PCILEECH_MEM_FLAG_RETRYONFAIL); + DeviceReadDMAEx(ctx, i, pb64M + i, 0x01000000, NULL); } // 1: search for string 'vn_open' i = 0; @@ -357,7 +354,7 @@ BOOL KMD_Linux46KernelSeekSignature(_Inout_ PPCILEECH_CONTEXT ctx, _Out_ PSIGNAT // read 16M of memory first, if KASLR read 2M chunks at top of analysis buffer (performance reasons). dwKernelBase = 0x01000000 + cKSlide * 0x00200000; // KASLR = 16M + ([RND:0..511] * 2M) ??? if(cKSlide == 0) { - DeviceReadDMA(ctx, dwKernelBase, pb, 0x01000000, PCILEECH_MEM_FLAG_RETRYONFAIL); + DeviceReadDMAEx(ctx, dwKernelBase, pb, 0x01000000, NULL); } else { memmove(pb, pb + 0x00200000, CONFIG_LINUX_SEEK_BUFFER_SIZE - 0x00200000); result = DeviceReadDMA( @@ -388,16 +385,16 @@ QWORD KMD_Linux48KernelBaseSeek(_Inout_ PPCILEECH_CONTEXT ctx) memset(pbCMP90, 0x90, 0x400); memset(pbCMP00, 0x00, 0x100); qwA = max(0x01000000, ctx->cfg->qwAddrMin) & 0xffffffffffe00000; - qwAddrMax = (ctx->cfg->tpDevice == PCILEECH_DEVICE_USB3380) ? - max(0x01000000, (ctx->cfg->qwAddrMax - 0x01000000) & 0xffe00000) : - max(0x01000000, (ctx->cfg->qwAddrMax - 0x01000000) & 0xffffffffffe00000); + qwAddrMax = max(0x01000000, (ctx->cfg->dev.qwAddrMaxNative - 0x01000000) & 0xffffffffffe00000); PageStatInitialize(&ps, qwA, qwAddrMax, "Scanning for Linux kernel base", FALSE, FALSE); // Linux kernel uses 2MB pages. Base of kernel is assumed to have AuthenticAMD and GenuineIntel strings // in first page. First page should also end with at least 0x400 0x90's. 2nd page (hypercall page?) is // assumed to end with 0x100 0x00's. for(; qwA <= qwAddrMax; qwA += 0x00200000) { ps.qwAddr = qwA; - if(!DeviceReadDMA(ctx, qwA, pb, ctx->cfg->fPartialPageReadSupported ? 0x400 : 0x1000, 0)) { // only read partial page to speed up SP605 if connected via UART + // only read partial page to speed up SP605 if connected via UART + // TODO: investigate if this is slower/faster than reading whole pages on FPGA solution. + if(!DeviceReadDMA(ctx, qwA, pb, ctx->cfg->fPartialPageReadSupported ? 0x400 : 0x1000, 0)) { ps.cPageFail += 512; continue; } @@ -576,8 +573,7 @@ BOOL KMDOpen_UEFI_FindEfiBase(_Inout_ PPCILEECH_CONTEXT ctx) PageStatInitialize(&ps, dwAddrCurrent, dwAddrMax, "Searching for EFI BASE", FALSE, FALSE); // loop EFI BASE (IBI SYST) find while(dwAddrCurrent <= dwAddrMax - 0x100000) { - if(DeviceReadDMA(ctx, dwAddrCurrent, pb, 0x100000, 0)) { - PageStatUpdate(&ps, dwAddrCurrent + 0x100000, 0x100, 0); + if(DeviceReadDMAEx(ctx, dwAddrCurrent, pb, 0x100000, &ps)) { for(o = 0; o < 0x100000 - 0x100; o += 8) { if(0x5453595320494249 != *(PQWORD)(pb + o)) { continue; } // IBI SYST qwAddr_BOOTSERV = *(PQWORD)(pb + o + 0x60); @@ -648,8 +644,8 @@ BOOL KMDOpen_UEFI(_Inout_ PPCILEECH_CONTEXT ctx, _In_ BYTE bOffsetHookBootServic // 3: Patch //------------------------------------------------ if(ctx->cfg->fVerbose) { - printf("INFO: IBI SYST: 0x%08x\n", ctx->cfg->qwEFI_IBI_SYST); - printf("INFO: BOOTSERV: 0x%08x\n", qwAddrEFI_BOOTSERV); + printf("INFO: IBI SYST: 0x%08x\n", (DWORD)ctx->cfg->qwEFI_IBI_SYST); + printf("INFO: BOOTSERV: 0x%08x\n", (DWORD)qwAddrEFI_BOOTSERV); } result = DeviceWriteDMA(ctx, qwAddrKMDDATA, pb, 0x2000, PCILEECH_MEM_FLAG_RETRYONFAIL); if(!result) { @@ -848,7 +844,7 @@ BOOL KMD_GetPhysicalMemoryMap(_Inout_ PPCILEECH_CONTEXT ctx) } ctx->phKMD->pPhysicalMap = LocalAlloc(LMEM_ZEROINIT, (ctx->pk->_size + 0x1000) & 0xfffff000); if(!ctx->phKMD->pPhysicalMap) { return FALSE; } - DeviceReadDMA(ctx, ctx->pk->DMAAddrPhysical, (PBYTE)ctx->phKMD->pPhysicalMap, (DWORD)((ctx->pk->_size + 0x1000) & 0xfffff000), 0); + DeviceReadDMAEx(ctx, ctx->pk->DMAAddrPhysical, (PBYTE)ctx->phKMD->pPhysicalMap, (DWORD)((ctx->pk->_size + 0x1000) & 0xfffff000), NULL); ctx->phKMD->cPhysicalMap = ctx->pk->_size / sizeof(PHYSICAL_MEMORY_RANGE); if(ctx->phKMD->cPhysicalMap > 0x2000) { return FALSE; } // adjust max memory according to physical memory @@ -904,7 +900,7 @@ BOOL KMDReadMemory_DMABufferSized(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAd if(!result) { return FALSE; } result = KMD_SubmitCommand(ctx, KMD_CMD_READ); if(!result) { return FALSE; } - return DeviceReadDMA(ctx, ctx->pk->DMAAddrPhysical, pb, cb, 0) && ctx->pk->_result; + return (cb == DeviceReadDMAEx(ctx, ctx->pk->DMAAddrPhysical, pb, cb, NULL)) && ctx->pk->_result; } BOOL KMDWriteMemory_DMABufferSized(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddress, _In_ PBYTE pb, _In_ DWORD cb) diff --git a/pcileech/memdump.c b/pcileech/memdump.c index 376d024..26fb69c 100644 --- a/pcileech/memdump.c +++ b/pcileech/memdump.c @@ -127,7 +127,7 @@ VOID ActionMemoryDump(_Inout_ PPCILEECH_CONTEXT ctx) } } -#define MEMORY_PROBE_PAGES_PER_SWEEP 0x400 +#define MEMORY_PROBE_PAGES_PER_SWEEP 0x1000 VOID ActionMemoryProbe(_Inout_ PPCILEECH_CONTEXT ctx) { @@ -214,7 +214,7 @@ VOID ActionMemoryWrite(_Inout_ PPCILEECH_CONTEXT ctx) printf("Memory Write: Failed. No data to write.\n"); return; } - if(ctx->cfg->cbIn >= 0x01000000) { + if(ctx->cfg->cbIn > 0x01000000) { printf("Memory Write: Failed. Data too large: >16MB.\n"); return; } diff --git a/pcileech/oscompatibility.c b/pcileech/oscompatibility.c index dc42673..7497ea5 100644 --- a/pcileech/oscompatibility.c +++ b/pcileech/oscompatibility.c @@ -3,6 +3,22 @@ // (c) Ulf Frisk, 2017 // Author: Ulf Frisk, pcileech@frizk.net // +#ifdef WIN32 + +#include "oscompatibility.h" + +VOID usleep(_In_ DWORD us) +{ + QWORD tmFreq, tmStart, tmNow, tmThreshold; + QueryPerformanceFrequency((PLARGE_INTEGER)&tmFreq); + tmThreshold = tmFreq * us / (1000 * 1000); // dw_uS uS + QueryPerformanceCounter((PLARGE_INTEGER)&tmStart); + while(QueryPerformanceCounter((PLARGE_INTEGER)&tmNow) && ((tmNow - tmStart) < tmThreshold)) { + ; + } +} + +#endif /* WIN32 */ #if defined(LINUX) || defined(ANDROID) #include "oscompatibility.h" @@ -135,6 +151,11 @@ BOOL WinUsb_Free(WINUSB_INTERFACE_HANDLE InterfaceHandle) return TRUE; } +DWORD InterlockedAdd(DWORD *Addend, DWORD Value) +{ + return __sync_add_and_fetch(Addend, Value); +} + #endif /* LINUX || ANDROID */ #ifdef LINUX diff --git a/pcileech/oscompatibility.h b/pcileech/oscompatibility.h index 2e7affc..8a5ae0d 100644 --- a/pcileech/oscompatibility.h +++ b/pcileech/oscompatibility.h @@ -23,6 +23,8 @@ typedef unsigned __int64 QWORD, *PQWORD; #pragma warning( disable : 4477) +VOID usleep(_In_ DWORD us); + #endif /* WIN32 */ #ifdef LINUX @@ -114,6 +116,7 @@ typedef uint64_t SIZE_T, *PSIZE_T; #define ZeroMemory(pb, cb) (memset(pb, 0, cb)) #define WinUsb_SetPipePolicy(h, p, t, cb, pb) // TODO: implement this for better USB2 performance. #define CloseHandle(h) // TODO: remove this dummy implementation & replace with WARN. +#define SetEvent(h) // TODO: remove this dummy implementation! typedef struct _SYSTEMTIME { WORD wYear; @@ -137,6 +140,7 @@ HANDLE LocalAlloc(DWORD uFlags, SIZE_T uBytes); VOID LocalFree(HANDLE hMem); QWORD GetTickCount64(); VOID GetLocalTime(LPSYSTEMTIME lpSystemTime); +DWORD InterlockedAdd(DWORD *Addend, DWORD Value); BOOL WinUsb_Free(WINUSB_INTERFACE_HANDLE InterfaceHandle); HANDLE CreateThread( diff --git a/pcileech/pcileech.c b/pcileech/pcileech.c index 52ce0d0..2bb26f3 100644 --- a/pcileech/pcileech.c +++ b/pcileech/pcileech.c @@ -6,7 +6,6 @@ #include "pcileech.h" #include "device.h" #include "device3380.h" -#include "device605.h" #include "executor.h" #include "extra.h" #include "help.h" @@ -16,7 +15,7 @@ #include "kmd.h" #include "vfs.h" -BOOL PCILeechInitializeConfig(_In_ DWORD argc, _In_ char* argv[], _Inout_ PPCILEECH_CONTEXT ctx) +BOOL PCILeechConfigIntialize(_In_ DWORD argc, _In_ char* argv[], _Inout_ PPCILEECH_CONTEXT ctx) { struct ACTION { ACTION_TYPE tp; @@ -44,7 +43,6 @@ BOOL PCILeechInitializeConfig(_In_ DWORD argc, _In_ char* argv[], _Inout_ PPCILE {.tp = TLP,.sz = "tlp" }, {.tp = PROBE,.sz = "probe" }, }; - QWORD qw; DWORD j, i = 1; if(argc < 2) { return FALSE; } // allocate memory for config struct @@ -55,7 +53,6 @@ BOOL PCILeechInitializeConfig(_In_ DWORD argc, _In_ char* argv[], _Inout_ PPCILE ctx->cfg->qwAddrMax = ~0; ctx->cfg->fOutFile = TRUE; ctx->cfg->qwMaxSizeDmaIo = ~0; - ctx->cfg->tpDevice = PCILEECH_DEVICE_USB3380; // fetch command line actions/options loop: while(i < argc) { @@ -114,14 +111,17 @@ BOOL PCILeechInitializeConfig(_In_ DWORD argc, _In_ char* argv[], _Inout_ PPCILE ctx->cfg->qwEFI_IBI_SYST = Util_GetNumeric(argv[i + 1]); } else if(0 == strcmp(argv[i], "-iosize")) { ctx->cfg->qwMaxSizeDmaIo = Util_GetNumeric(argv[i + 1]); - } else if(0 == strcmp(argv[i], "-wait")) { - ctx->cfg->qwWaitBeforeExit = Util_GetNumeric(argv[i + 1]); + ctx->cfg->qwMaxSizeDmaIo = ~0xfff & max(0x1000, ctx->cfg->qwMaxSizeDmaIo); + } else if(0 == strcmp(argv[i], "-tlpwait")) { + ctx->cfg->dwListenTlpTimeMs = (DWORD)(1000 * Util_GetNumeric(argv[i + 1])); } else if(0 == strcmp(argv[i], "-device")) { - ctx->cfg->tpDevice = PCILEECH_DEVICE_NA; + ctx->cfg->dev.tp = PCILEECH_DEVICE_NA; if(0 == _stricmp(argv[i + 1], "usb3380")) { - ctx->cfg->tpDevice = PCILEECH_DEVICE_USB3380; - } else if(0 == _stricmp(argv[i + 1], "sp605")) { - ctx->cfg->tpDevice = PCILEECH_DEVICE_SP605; + ctx->cfg->dev.tp = PCILEECH_DEVICE_USB3380; + } else if(0 == _stricmp(argv[i + 1], "sp605_uart")) { + ctx->cfg->dev.tp = PCILEECH_DEVICE_SP605_UART; + } else if(0 == _stricmp(argv[i + 1], "sp605_ft601")) { + ctx->cfg->dev.tp = PCILEECH_DEVICE_SP605_FT601; } } else if(0 == strcmp(argv[i], "-out")) { if((0 == _stricmp(argv[i + 1], "none")) || (0 == _stricmp(argv[i + 1], "null"))) { @@ -130,7 +130,10 @@ BOOL PCILeechInitializeConfig(_In_ DWORD argc, _In_ char* argv[], _Inout_ PPCILE strcpy_s(ctx->cfg->szFileOut, MAX_PATH, argv[i + 1]); } } else if(0 == strcmp(argv[i], "-in")) { - if(!Util_ParseHexFileBuiltin(argv[i + 1], ctx->cfg->pbIn, CONFIG_MAX_INSIZE, (PDWORD)&ctx->cfg->cbIn)) { return FALSE; } + ctx->cfg->cbIn = max(0x40000, 0x1000 + Util_GetFileSize(argv[i + 1])); + ctx->cfg->pbIn = LocalAlloc(LMEM_ZEROINIT, ctx->cfg->cbIn); + if(!ctx->cfg->pbIn) { return FALSE; } + if(!Util_ParseHexFileBuiltin(argv[i + 1], ctx->cfg->pbIn, (DWORD)ctx->cfg->cbIn, (PDWORD)&ctx->cfg->cbIn)) { return FALSE; } } else if(0 == strcmp(argv[i], "-s")) { strcpy_s(ctx->cfg->szInS, MAX_PATH, argv[i + 1]); } else if(0 == strcmp(argv[i], "-sig")) { @@ -147,24 +150,25 @@ BOOL PCILeechInitializeConfig(_In_ DWORD argc, _In_ char* argv[], _Inout_ PPCILE } i += 2; } + if(!ctx->cfg->pbIn) { + ctx->cfg->pbIn = LocalAlloc(LMEM_ZEROINIT, 0x40000); + } // try correct erroneous options, if needed - if((ctx->cfg->tpAction == NA) || (ctx->cfg->tpDevice == PCILEECH_DEVICE_NA)) { + if(ctx->cfg->tpAction == NA) { return FALSE; } + return TRUE; +} + +VOID PCILeechConfigFixup(_Inout_ PPCILEECH_CONTEXT ctx) +{ + QWORD qw; // device specific configuration - if(ctx->cfg->tpDevice == PCILEECH_DEVICE_USB3380) { - ctx->cfg->qwAddrMaxDeviceNative = 0xffffffff; - ctx->cfg->qwMaxSizeDmaIo = min(0x01000000, ctx->cfg->qwMaxSizeDmaIo); - } - if(ctx->cfg->tpDevice == PCILEECH_DEVICE_SP605) { - ctx->cfg->qwAddrMaxDeviceNative = 0x0000ffffffffffff; - ctx->cfg->qwMaxSizeDmaIo = min(0x00004000, ctx->cfg->qwMaxSizeDmaIo); - ctx->cfg->fPartialPageReadSupported = TRUE; - } + ctx->cfg->qwMaxSizeDmaIo = min(ctx->cfg->qwMaxSizeDmaIo, ctx->cfg->dev.qwMaxSizeDmaIo); // no kmd -> max address == max address that device support if(!ctx->cfg->szKMDName[0] && !ctx->cfg->qwKMD) { - if(ctx->cfg->qwAddrMax == 0 || ctx->cfg->qwAddrMax > ctx->cfg->qwAddrMaxDeviceNative) { - ctx->cfg->qwAddrMax = ctx->cfg->qwAddrMaxDeviceNative; + if(ctx->cfg->qwAddrMax == 0 || ctx->cfg->qwAddrMax > ctx->cfg->dev.qwAddrMaxNative) { + ctx->cfg->qwAddrMax = ctx->cfg->dev.qwAddrMaxNative; } } // fixup addresses @@ -175,7 +179,6 @@ BOOL PCILeechInitializeConfig(_In_ DWORD argc, _In_ char* argv[], _Inout_ PPCILE } ctx->cfg->qwCR3 &= ~0xfff; ctx->cfg->qwKMD &= ~0xfff; - return TRUE; } VOID PCILeechFreeContext(_Inout_ PPCILEECH_CONTEXT ctx) @@ -183,7 +186,10 @@ VOID PCILeechFreeContext(_Inout_ PPCILEECH_CONTEXT ctx) if(!ctx) { return; } KMDClose(ctx); DeviceClose(ctx); - if(ctx->cfg) { LocalFree(ctx->cfg); } + if(ctx->cfg) { + if(ctx->cfg->pbIn) { LocalFree(ctx->cfg->pbIn); } + LocalFree(ctx->cfg); + } if(ctx) { LocalFree(ctx); } } @@ -198,11 +204,11 @@ int main(_In_ int argc, _In_ char* argv[]) printf("PCILEECH: Out of memory.\n"); return 1; } - result = PCILeechInitializeConfig((DWORD)argc, argv, ctx); + result = PCILeechConfigIntialize((DWORD)argc, argv, ctx); if(!result) { Help_ShowGeneral(); PCILeechFreeContext(ctx); - return FALSE; + return 1; } if(ctx->cfg->tpAction == EXEC) { result = Util_LoadKmdExecShellcode(ctx->cfg->szShellcodeName, &pKmdExec); @@ -234,6 +240,7 @@ int main(_In_ int argc, _In_ char* argv[]) PCILeechFreeContext(ctx); return 1; } + PCILeechConfigFixup(ctx); // post device config adjustments if(ctx->cfg->szKMDName[0] || ctx->cfg->qwKMD) { result = KMDOpen(ctx); if(!result) { @@ -267,7 +274,7 @@ int main(_In_ int argc, _In_ char* argv[]) } else if(ctx->cfg->tpAction == PT_PHYS2VIRT) { Action_PT_Phys2Virt(ctx); } else if(ctx->cfg->tpAction == TLP) { - Action_Device605_TlpTx(ctx); + Action_TlpTx(ctx); } else if(ctx->cfg->tpAction == PROBE) { ActionMemoryProbe(ctx); } else if(ctx->cfg->tpAction == MOUNT) { @@ -284,11 +291,11 @@ int main(_In_ int argc, _In_ char* argv[]) } else { printf("Failed. Not yet implemented.\n"); } - if((ctx->cfg->tpAction != KMDLOAD) && !ctx->cfg->fAddrKMDSetByArgument) { + if(ctx->phKMD && (ctx->cfg->tpAction != KMDLOAD) && !ctx->cfg->fAddrKMDSetByArgument) { KMDUnload(ctx); printf("KMD: Hopefully unloaded.\n"); } - Sleep(1000 * (DWORD)ctx->cfg->qwWaitBeforeExit); + DeviceListenTlp(ctx, ctx->cfg->dwListenTlpTimeMs); PCILeechFreeContext(ctx); ExitProcess(0); return 0; diff --git a/pcileech/pcileech.h b/pcileech/pcileech.h index 219adae..70772e3 100644 --- a/pcileech/pcileech.h +++ b/pcileech/pcileech.h @@ -17,6 +17,8 @@ typedef struct tdSignaturePTE { } SIGNATUREPTE, *PSIGNATUREPTE; #pragma pack(pop) /* RE-ENABLE STRUCT PADDINGS */ +typedef struct tdPCILEECH_CONTEXT PCILEECH_CONTEXT, *PPCILEECH_CONTEXT; + typedef enum tdActionType { NA, INFO, @@ -45,40 +47,54 @@ typedef enum tdActionType { typedef enum tdPCILEECH_DEVICE_TYPE { PCILEECH_DEVICE_NA, PCILEECH_DEVICE_USB3380, - PCILEECH_DEVICE_SP605 + PCILEECH_DEVICE_SP605_UART, + PCILEECH_DEVICE_SP605_FT601 } PCILEECH_DEVICE_TYPE; -#define CONFIG_MAX_INSIZE 0x400000 // 4MB +typedef struct tdDeviceConfig { + QWORD qwMaxSizeDmaIo; + QWORD qwAddrMaxNative; + PCILEECH_DEVICE_TYPE tp; + BOOL fPartialPageReadSupported; + BOOL(*pfnReadDMA)(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb); + BOOL(*pfnWriteDMA)(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb); + VOID(*pfnProbeDMA)(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap); + BOOL(*pfnWriteTlp)(_Inout_ PPCILEECH_CONTEXT ctx, _In_ PBYTE pb, _In_ DWORD cb); + BOOL(*pfnListenTlp)(_Inout_ PPCILEECH_CONTEXT ctx, _In_ DWORD dwTime); + VOID(*pfnClose)(_Inout_ PPCILEECH_CONTEXT ctx); +} DEVICE_CONFIG; + typedef struct tdConfig { QWORD qwAddrMin; QWORD qwAddrMax; - QWORD qwAddrMaxDeviceNative; QWORD qwCR3; QWORD qwEFI_IBI_SYST; QWORD qwKMD; CHAR szFileOut[MAX_PATH]; - BYTE pbIn[CONFIG_MAX_INSIZE]; + PBYTE pbIn; QWORD cbIn; CHAR szInS[MAX_PATH]; QWORD qwDataIn[10]; ACTION_TYPE tpAction; - PCILEECH_DEVICE_TYPE tpDevice; CHAR szSignatureName[MAX_PATH]; CHAR szKMDName[MAX_PATH]; CHAR szShellcodeName[MAX_PATH]; QWORD qwMaxSizeDmaIo; - QWORD qwWaitBeforeExit; + DWORD dwListenTlpTimeMs; + // flags below BOOL fPageTableScan; BOOL fPatchAll; BOOL fForceRW; BOOL fShowHelp; BOOL fOutFile; - BOOL fForceUsb2; + BOOL fForceUsb2; // USB3380 BOOL fVerbose; BOOL fVerboseExtra; BOOL fDebug; BOOL fPartialPageReadSupported; BOOL fAddrKMDSetByArgument; + // device information below + DEVICE_CONFIG dev; } CONFIG, *PCONFIG; #define SIGNATURE_CHUNK_TP_OFFSET_FIXED 0 @@ -193,11 +209,11 @@ typedef struct tdKMDHANDLE { BYTE pbPageData[4096]; } KMDHANDLE, *PKMDHANDLE; -typedef struct tdPCILEECH_CONTEXT { +struct tdPCILEECH_CONTEXT { PCONFIG cfg; HANDLE hDevice; PKMDHANDLE phKMD; PKMDDATA pk; -} PCILEECH_CONTEXT, *PPCILEECH_CONTEXT; +}; #endif /* __PCILEECH_H__ */ diff --git a/pcileech/pcileech.vcxproj b/pcileech/pcileech.vcxproj index 16b4132..475b87b 100644 --- a/pcileech/pcileech.vcxproj +++ b/pcileech/pcileech.vcxproj @@ -85,7 +85,8 @@ - + + @@ -104,7 +105,8 @@ - + + diff --git a/pcileech/pcileech.vcxproj.filters b/pcileech/pcileech.vcxproj.filters index 5029427..78612b6 100644 --- a/pcileech/pcileech.vcxproj.filters +++ b/pcileech/pcileech.vcxproj.filters @@ -57,9 +57,6 @@ Header Files\dokan - - Header Files - Header Files @@ -69,6 +66,12 @@ Header Files + + Header Files + + + Header Files + @@ -104,9 +107,6 @@ Source Files - - Source Files - Source Files @@ -116,6 +116,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/pcileech/statistics.c b/pcileech/statistics.c index ecf8fb6..39e2039 100644 --- a/pcileech/statistics.c +++ b/pcileech/statistics.c @@ -28,7 +28,7 @@ VOID _PageStatPrintMemMap(_Inout_ PPAGE_STATISTICS ps) if(!ps->i.MemMap[i] || (i == PAGE_STATISTICS_MEM_MAP_MAX_ENTRY - 1)) { break; } - qwAddrEnd = qwAddrBase + ps->i.MemMap[i] * 0x1000; + qwAddrEnd = qwAddrBase + 0x1000 * (QWORD)ps->i.MemMap[i]; if((i % 2) == 0) { fIsLinePrinted = TRUE; printf( diff --git a/pcileech/statistics.h b/pcileech/statistics.h index 7edcb8f..65af539 100644 --- a/pcileech/statistics.h +++ b/pcileech/statistics.h @@ -7,7 +7,7 @@ #define __STATISTICS_H__ #include "pcileech.h" -#define PAGE_STATISTICS_MEM_MAP_MAX_ENTRY 512 +#define PAGE_STATISTICS_MEM_MAP_MAX_ENTRY 1024 typedef struct tdPageStatistics { QWORD qwAddr; diff --git a/pcileech/tlp.c b/pcileech/tlp.c index 6f2de73..7e7cbf9 100644 --- a/pcileech/tlp.c +++ b/pcileech/tlp.c @@ -17,7 +17,7 @@ VOID TLP_Print(_In_ PBYTE pbTlp, _In_ DWORD cbTlp, _In_ BOOL isTx) PTLP_HDR_MRdWr64 hdrM64; if(cbTlp < 12 || cbTlp > 0x1000 || cbTlp & 0x3) { return; } for(i = 0; i < cbTlp; i += 4) { - buf[i] = _byteswap_ulong(*(PDWORD)(pbTlp + i)); + buf[i >> 2] = _byteswap_ulong(*(PDWORD)(pbTlp + i)); } printf("%s_TLP: TypeFmt: %02x Length: %03x(%04x)", (isTx ? "TX" : "RX"), hdr->TypeFmt, hdr->Length, (hdr->Length << 2)); if(hdr->TypeFmt == TLP_CplD) { @@ -28,8 +28,67 @@ VOID TLP_Print(_In_ PBYTE pbTlp, _In_ DWORD cbTlp, _In_ BOOL isTx) printf("\n%s: ReqID: %04x BE1: %01x BEL: %01x Tag: %02x Addr: %08x", (hdr->TypeFmt == TLP_MRd32) ? "MRd32" : "MWr32", hdrM32->RequesterID, hdrM32->FirstBE, hdrM32->LastBE, hdrM32->Tag, hdrM32->Address); } else if(hdr->TypeFmt == TLP_MRd64 || hdr->TypeFmt == TLP_MWr64) { hdrM64 = (PTLP_HDR_MRdWr64)pb; - printf("\n%s: ReqID: %04x BE1: %01x BEL: %01x Tag: %02x Addr: %016llx", (hdr->TypeFmt == TLP_MRd32) ? "MRr64" : "MWr64", hdrM64->RequesterID, hdrM64->FirstBE, hdrM64->LastBE, hdrM64->Tag, ((QWORD)hdrM64->AddressHigh << 32) + hdrM64->AddressLow); + printf("\n%s: ReqID: %04x BE1: %01x BEL: %01x Tag: %02x Addr: %016llx", (hdr->TypeFmt == TLP_MRd64) ? "MRd64" : "MWr64", hdrM64->RequesterID, hdrM64->FirstBE, hdrM64->LastBE, hdrM64->Tag, ((QWORD)hdrM64->AddressHigh << 32) + hdrM64->AddressLow); } printf("\n"); Util_PrintHexAscii(pbTlp, cbTlp); } + +BOOL TLP_CallbackMRd(_Inout_ PTLP_CALLBACK_BUF_MRd pBufferMRd, _In_ PBYTE pb, _In_ DWORD cb, _In_opt_ HANDLE hEventCompleted) +{ + PTLP_HDR_CplD hdrC = (PTLP_HDR_CplD)pb; + PTLP_HDR hdr = (PTLP_HDR)pb; + PDWORD buf = (PDWORD)pb; + DWORD o, c; + buf[0] = _byteswap_ulong(buf[0]); + if(cb < ((DWORD)hdr->Length << 2) - 12) { return FALSE; } + if((hdr->TypeFmt == TLP_CplD) && pBufferMRd) { + buf[1] = _byteswap_ulong(buf[1]); + buf[2] = _byteswap_ulong(buf[2]); + // NB! read algorithm below only support reading full 4kB pages _or_ + // partial page if starting at page boundry and read is less than 4kB. + o = ((DWORD)hdrC->Tag << 12) + min(0x1000, pBufferMRd->cbMax) - (hdrC->ByteCount ? hdrC->ByteCount : 0x1000); + c = (DWORD)hdr->Length << 2; + if(cb != c + 12) { return FALSE; } + if(o + c <= pBufferMRd->cbMax) { + memcpy(pBufferMRd->pb + o, pb + 12, c); + if(pBufferMRd->cbMax <= (DWORD)InterlockedAdd(&pBufferMRd->cb, c)) { + if(hEventCompleted) { + SetEvent(hEventCompleted); + } + return TRUE; + } + } + } else { + QWORD qwDebug[16]; + memset(qwDebug, 0xcc, 16 * 8); + memcpy(qwDebug, pb, min(cb, 16 * 8)); + DWORD DEBUGX = 0x01; + } + return FALSE; +} + +BOOL TLP_CallbackMRdProbe(_Inout_ PTLP_CALLBACK_BUF_MRd pBufferMRd, _In_ PBYTE pb, _In_ DWORD cb, _In_opt_ HANDLE hEventCompleted) +{ + PTLP_HDR_CplD hdrC = (PTLP_HDR_CplD)pb; + PDWORD buf = (PDWORD)pb; + DWORD i; + if(cb < 16) { return FALSE; } // min size CplD = 16 bytes. + buf[0] = _byteswap_ulong(buf[0]); + buf[1] = _byteswap_ulong(buf[1]); + buf[2] = _byteswap_ulong(buf[2]); + if((hdrC->h.TypeFmt == TLP_CplD) && pBufferMRd) { + // 5 low address bits coded into the dword read, 8 high address bits coded into tag. + i = ((DWORD)hdrC->Tag << 5) + ((hdrC->LowerAddress >> 2) & 0x1f); + if(i < pBufferMRd->cbMax) { + pBufferMRd->pb[i] = 1; + if(pBufferMRd->cbMax <= (DWORD)InterlockedAdd(&pBufferMRd->cb, 1)) { + if(hEventCompleted) { + SetEvent(hEventCompleted); + } + return TRUE; + } + } + } + return FALSE; +} diff --git a/pcileech/tlp.h b/pcileech/tlp.h index 011f001..35add20 100644 --- a/pcileech/tlp.h +++ b/pcileech/tlp.h @@ -1,4 +1,4 @@ -// device605.h : definitions related PCIe TLPs (transaction layper packets). +// tlp.h : definitions related PCIe TLPs (transaction layper packets). // // (c) Ulf Frisk, 2017 // Author: Ulf Frisk, pcileech@frizk.net @@ -75,4 +75,32 @@ typedef struct tdTLP_HDR_CplD { */ VOID TLP_Print(_In_ PBYTE pbTlp, _In_ DWORD cbTlp, _In_ BOOL isTx); +typedef struct tdTLP_CALLBACK_BUF_MRd { + DWORD cbMax; + DWORD cb; + PBYTE pb; +} TLP_CALLBACK_BUF_MRd, *PTLP_CALLBACK_BUF_MRd; + +/* +* Generic callback function that may be used by TLP capable devices to aid the +* collection of memory read completions. Receives single TLP packet. +* -- pBufferMrd +* -- pb +* -- cb +* -- hEventCompleted = optional Event that will be signaled once all MRd's are completed. +* -- return = TRUE if all required TLPs required to fill up buffer have been processed, otherwise FALSE. +*/ +BOOL TLP_CallbackMRd(_Inout_ PTLP_CALLBACK_BUF_MRd pBufferMrd, _In_ PBYTE pb, _In_ DWORD cb, _In_opt_ HANDLE hEventCompleted); + +/* +* Generic callback function that may be used by TLP capable devices to aid the +* collection of completions from the probe function. Receives single TLP packet. +* -- pBufferMrd +* -- pb +* -- cb +* -- hEventCompleted = optional Event that will be signaled once all MRd's are completed. +* -- return = TRUE if all required TLPs required TLPs have been processed, otherwise FALSE. +*/ +BOOL TLP_CallbackMRdProbe(_Inout_ PTLP_CALLBACK_BUF_MRd pBufferMRd, _In_ PBYTE pb, _In_ DWORD cb, _In_opt_ HANDLE hEventCompleted); + #endif /* __TLP_H__ */ diff --git a/pcileech/util.c b/pcileech/util.c index 9598c48..63162ff 100644 --- a/pcileech/util.c +++ b/pcileech/util.c @@ -317,6 +317,17 @@ BOOL Util_HexAsciiToBinary(_In_ LPSTR sz, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ P return TRUE; } +DWORD Util_GetFileSize(_In_ LPSTR sz) +{ + FILE *pFile; + DWORD size; + if(fopen_s(&pFile, sz, "rb") || !pFile) { return 0; } + fseek(pFile, 0, SEEK_END); // seek to end of file + size = ftell(pFile); // get current file pointer + fclose(pFile); + return size; +} + BOOL Util_ParseHexFileBuiltin(_In_ LPSTR sz, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcb) { SIZE_T i; @@ -401,7 +412,7 @@ BOOL Util_LoadSignatures(_In_ LPSTR szSignatureName, _In_ LPSTR szFileExtension, fclose(pFile); if(!cbFile || cbFile == 0x10000) { return FALSE; } // parse file - szLine = strtok_s(pbFile, "\r\n", &szContext); + szLine = strtok_s((char*)pbFile, "\r\n", &szContext); while(szLine && cSignatureIdx < *cSignatures) { if(Util_ParseSignatureLine(szLine, cSignatureChunks, pSignatures[cSignatureIdx].chunk)) { cSignatureIdx++; @@ -599,6 +610,9 @@ BOOL Util_Read16M(_Inout_ PPCILEECH_CONTEXT ctx, _Out_ PBYTE pbBuffer16M, _In_ Q { BOOL isSuccess[4] = { FALSE, FALSE, FALSE, FALSE }; QWORD i, o, qwOffset; + if(!ctx->phKMD) { // Native DMA + return 0 != DeviceReadDMAEx(ctx, qwBaseAddress, pbBuffer16M, 0x01000000, pPageStat); + } // try read 16M if((qwBaseAddress + 0x01000000 <= ctx->cfg->qwAddrMax) && DeviceReadMEM(ctx, qwBaseAddress, pbBuffer16M, 0x01000000, 0)) { PageStatUpdate(pPageStat, qwBaseAddress + 0x010000000, 4096, 0); @@ -610,16 +624,11 @@ BOOL Util_Read16M(_Inout_ PPCILEECH_CONTEXT ctx, _Out_ PBYTE pbBuffer16M, _In_ Q o = 0x00400000 * i; isSuccess[i] = (qwBaseAddress + o + 0x00400000 <= ctx->cfg->qwAddrMax) && DeviceReadMEM(ctx, qwBaseAddress + o, pbBuffer16M + o, 0x00400000, 0); } - // DMA mode + all memory inside scope + and all 4M reads fail + no force flag + iosize >= 1MB => fail - if(!ctx->cfg->fForceRW && !ctx->phKMD && (ctx->cfg->qwMaxSizeDmaIo >= 0x01000000) && qwBaseAddress + 0x01000000 <= ctx->cfg->qwAddrMax && !isSuccess[0] && !isSuccess[1] && !isSuccess[2] && !isSuccess[3]) { - PageStatUpdate(pPageStat, qwBaseAddress + 0x010000000, 0, 4096); - return FALSE; - } - // try read failed 4M chunks in 1M chunks + // try read failed chunks. for(i = 0; i < 4; i++) { if(isSuccess[i]) { PageStatUpdate(pPageStat, qwBaseAddress + (i + 1) * 0x00400000, 1024, 0); - } else { + } else { qwOffset = 0x00400000 * i; for(o = 0; o < 0x00400000; o += 0x00100000) { Util_Read1M(ctx, pbBuffer16M + qwOffset + o, qwBaseAddress + qwOffset + o, pPageStat); diff --git a/pcileech/util.h b/pcileech/util.h index a091f8a..4187ac2 100644 --- a/pcileech/util.h +++ b/pcileech/util.h @@ -108,6 +108,13 @@ VOID Util_GenRandom(_Out_ PBYTE pb, _In_ DWORD cb); */ BOOL Util_LoadKmdExecShellcode(_In_ LPSTR szKmdExecName, _Out_ PKMDEXEC* ppKmdExec); +/* +* Retrieve the file size of the file. If the file isn't found 0 is returned. +* -- sz = file to retrieve size of. +* -- return = file size, or 0 on fail. +*/ +DWORD Util_GetFileSize(_In_ LPSTR sz); + /* * Parse an input line consisting of either builtin, hexascii or file name to * data buffer. diff --git a/pcileech/vfs.c b/pcileech/vfs.c index ce3b586..0aa5a4b 100644 --- a/pcileech/vfs.c +++ b/pcileech/vfs.c @@ -358,7 +358,7 @@ BOOL UnicodeToAscii(_Out_ LPSTR szDst, _In_ SIZE_T cDst, _In_ LPCWSTR wcsSrc) VOID Vfs_UtilSplitPathFile(_Out_ WCHAR wszPath[MAX_PATH], _Out_ LPWSTR *pwcsFile, _In_ LPCWSTR wcsFileName) { DWORD i, iSplitFilePath; - wcscpy_s(wszPath, MAX_PATH, wcsFileName); + wcsncpy_s(wszPath, MAX_PATH, wcsFileName, _TRUNCATE); for(i = 0; i < MAX_PATH; i++) { if(wszPath[i] == '\\') { iSplitFilePath = i; diff --git a/pcileech_files/pcileech b/pcileech_files/pcileech index cec7f31..09e7620 100644 Binary files a/pcileech_files/pcileech and b/pcileech_files/pcileech differ diff --git a/pcileech_files/pcileech.exe b/pcileech_files/pcileech.exe index 0813a3b..33af53d 100644 Binary files a/pcileech_files/pcileech.exe and b/pcileech_files/pcileech.exe differ diff --git a/readme.md b/readme.md index e861324..15b65fd 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ PCILeech Summary: ================= PCILeech uses PCIe hardware devices to read and write from the target system memory. This is achieved by using DMA over PCIe. No drivers are needed on the target system. -PCILeech supports multiple hardware. Currently only the USB3380 hardware is publically available. The USB3380 is only able to read 4GB of memory natively, but is able to read all memory if a kernel module (KMD) is first inserted into the target system kernel. +PCILeech supports multiple hardware. USB3380 based hardware is only able to read 4GB of memory natively, but is able to read all memory if a kernel module (KMD) is first inserted into the target system kernel. FPGA based hardware is able to read all memory. PCILeech is capable of inserting a wide range of kernel implants into the targeted kernels - allowing for easy access to live ram and the file system via a "mounted drive". It is also possible to remove the logon password requirement, loading unsigned drivers, executing code and spawn system shells. PCIleech runs on Windows/Linux/Android. Supported target systems are currently the x64 versions of: UEFI, Linux, FreeBSD, macOS and Windows. @@ -10,10 +10,12 @@ PCILeech is capable of inserting a wide range of kernel implants into the target Capabilities: ============= -* Retrieve memory from the target system at >150MB/s. +* Retrieve memory from the target system at >150MB/s. * Write data to the target system memory. -* 4GB memory can be accessed in native DMA mode. +* 4GB memory can be accessed in native DMA mode (USB3380 hardware). +* ALL memory can be accessed in native DMA mode (FPGA hardware). * ALL memory can be accessed if kernel module (KMD) is loaded. +* Raw PCIe TLP access (FPGA hardware). * Mount live RAM as file [Linux, Windows, macOS]. * Mount file system as drive [Linux, Windows, macOS]. * Execute kernel code on the target system. @@ -33,7 +35,9 @@ Please ensure you do have the most recent version of PCILeech by visiting the PC Clone the PCILeech Github repository. The binaries are found in pcileech_files and should work on 64-bit Windows and Linux. Please copy all files from pcileech_files since some files contains additional modules and signatures. #### Windows: -The Google Android USB driver also have to be installed. Download the Google Android USB driver from: http://developer.android.com/sdk/win-usb.html#download Unzip the driver. Open Device Manager. Right click on the computer, choose add legacy hardware. Select install the hardware manually. Click Have Disk. Navigate to the Android Driver, select android_winusb.inf and install. +The Google Android USB driver also have to be installed if USB3380 hardware is used. Download the Google Android USB driver from: http://developer.android.com/sdk/win-usb.html#download Unzip the driver. Open Device Manager. Right click on the computer, choose add legacy hardware. Select install the hardware manually. Click Have Disk. Navigate to the Android Driver, select android_winusb.inf and install. + +FTDI drivers have to be installed if FPGA is used with FT601 USB3 addon card. FTDI drivers will installed automatically on Windows from Windows Update at first connection. PCILeech also requires 64-bit [`FTD3XX.dll`](http://www.ftdichip.com/Drivers/D3XX/FTD3XXLibrary_v1.2.0.6.zip) which must be downloaded from FTDI and placed alongside `pcileech.exe`. To mount live ram and target file system as drive in Windows the Dokany file system library must be installed. Please download and install the latest version of Dokany at: https://github.com/dokan-dev/dokany/releases/latest @@ -43,7 +47,11 @@ PCILeech on Linux must be run as root. PCILeech also requires libusb. Libusb is #### Android: Separate instructions for [Android](Android.md). -Hardware: +Hardware (FPGA): +================= +Please check out the [PCILeech FPGA project](https://github.com/ufrisk/pcileech-fpga/) for information about supported FPGA based hardware. + +Hardware (USB3380): ================= PCILeech use the PLX Technologies USB3380 chip. The actual chip can be purchased for around $15, but it's more convenient to purchase a development board on which the chip is already mounted. Development boards can be purchased from BPlus Technology, or on eBay / Ali Express. Please note that adapters may be required too depending on your requirements. In addition to the USB3380 PCILeech also supports not yet released FPGA based hardware. @@ -67,7 +75,7 @@ Recommended adapters: Please note that other adapters may also work. -Flashing Hardware: +Flashing Hardware (USB3380): ================== In order to turn the USB3380 development board into a PCILeech device it must be flashed. Flashing may be done in Windows 10 (as administrator) or in Linux (as root). The board must be connected to the system via PCIe when performing the initial flash. @@ -118,7 +126,7 @@ Dump all memory from the target system given that a kernel module is loaded at a Force dump memory below 4GB including accessible memory mapped devices using more stable USB2 approach. * ` pcileech.exe dump -force -usb2 ` -Exploit a vulnerable mac to retrieve the FileVault2 password. +Exploit a vulnerable mac to retrieve the FileVault2 password. (USB3380 only). * ` pcileech.exe mac_fvrecover ` Receive PCIe TLPs (Transaction Layer Packets) and print them on screen (correctly configured FPGA dev board required). @@ -130,6 +138,15 @@ Load a "kernel" module by searching for and hooking UEFI BootServices.SignalEven Load a "kernel" module by hooking and BootServices.ExitBootServices(). Base memory location of UEFI specified manually (IBI SYST table). * ` pcileech.exe kmdload -kmd UEFI_EXIT_BOOT_SERVICES -efibase 0x7b399018 ` +Probe/Enumerate the memory of the target system for readable memory pages and maximum memory. (FPGA hardware only). +* ` pcileech.exe probe ` + +Dump all memory between addresses min and max, don't stop on failed pages. Native access to 64-bit memory is only supported on FPGA hardware. +* ` pcileech.exe dump -min 0x0 -max 0x21e5fffff -force ` + +Force the usage of a specific device (instead of default auto detecting it). +* ` pcileech.exe pagedisplay -min 0x1000 -device usb3380 ` + Generating Signatures: ====================== PCILeech comes with built in signatures for Windows, Linux, FreeBSD and macOS. For Windows 8.1 or later it is also possible to use the pcileech_gensig.exe program to generate alternative signatures. @@ -143,6 +160,7 @@ Limitations/Known Issues: * Windows Vista: some shellcode modules such as wx64_pscmd does not work. * Windows 7: signatures are not published. * The Linux/Android versions of PCILeech dumps memory slightly slower than the Windows version. Mount target file system and live RAM are also not availabe in the Linux/Android versions. +* FPGA support currently only exists if PCILeech is run on Windows. Linux and Android support is planned for the future. Building: ========= @@ -179,3 +197,7 @@ v2.2 * UEFI support. * Linux 2.6.33-4.6 target support. * signature: Windows 10 updates to pcileech_gensig.exe + +v2.3 +* [FPGA hardware support (SP605/FT601)](https://github.com/ufrisk/pcileech-fpga). +* Various changes.