diff --git a/pcileech/Android.mk b/pcileech/Android.mk index 626397b..18ee256 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_601.c device605_tcp.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 devicefpga.c device605_tcp.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 b6eb9c8..bd01240 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_601.o device605_tcp.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 devicefpga.o device605_tcp.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 0eacdd6..36ee6bb 100644 --- a/pcileech/device.c +++ b/pcileech/device.c @@ -1,13 +1,13 @@ // device.c : implementation related to hardware devices. // -// (c) Ulf Frisk, 2016, 2017 +// (c) Ulf Frisk, 2016-2018 // Author: Ulf Frisk, pcileech@frizk.net // #include "device.h" #include "kmd.h" #include "statistics.h" #include "device3380.h" -#include "device605_601.h" +#include "devicefpga.h" #include "device605_tcp.h" typedef struct tdREAD_DMA_EX_MEMORY_MAP { @@ -58,6 +58,7 @@ DWORD DeviceReadDMAEx_DoWork(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _ DeviceReadDMAEx_IsMemoryMapOK(pMemoryMap, qwAddr + cbRdOff, cbRd) && DeviceReadDMA(ctx, qwAddr + cbRdOff, pb + cbRdOff, cbRd, 0); if(!result && !pMemoryMap->fProbeExecuted && ctx->cfg->dev.pfnProbeDMA) { // probe memory on 1st fail (if supported) + memset(pMemoryMap->pb, 0, pMemoryMap->cPages); DeviceProbeDMA(ctx, pMemoryMap->qwAddrBase, pMemoryMap->cPages, pMemoryMap->pb); pMemoryMap->fProbeExecuted = TRUE; } @@ -121,7 +122,7 @@ BOOL DeviceWriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE return result; } -BOOL DeviceProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap) +BOOL DeviceProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Inout_ __bcount(cPages) PBYTE pbResultMap) { if(!ctx->cfg->dev.pfnProbeDMA) { return FALSE; } ctx->cfg->dev.pfnProbeDMA(ctx, qwAddr, cPages, pbResultMap); @@ -153,8 +154,8 @@ BOOL DeviceOpen(_Inout_ PPCILEECH_CONTEXT ctx) if(PCILEECH_DEVICE_USB3380 == ctx->cfg->dev.tp || PCILEECH_DEVICE_NA == ctx->cfg->dev.tp) { result = Device3380_Open(ctx); } - if(PCILEECH_DEVICE_SP605_FT601 == ctx->cfg->dev.tp || PCILEECH_DEVICE_NA == ctx->cfg->dev.tp) { - result = Device605_601_Open(ctx); + if(PCILEECH_DEVICE_FPGA == ctx->cfg->dev.tp || PCILEECH_DEVICE_NA == ctx->cfg->dev.tp) { + result = DeviceFPGA_Open(ctx); } if(PCILEECH_DEVICE_SP605_TCP == ctx->cfg->dev.tp) { result = Device605_TCP_Open(ctx); diff --git a/pcileech/device.h b/pcileech/device.h index c9007e6..13a586f 100644 --- a/pcileech/device.h +++ b/pcileech/device.h @@ -71,9 +71,11 @@ BOOL DeviceWriteDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ PBYTE * -- 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. +* (individual page elements in pbResultMap must be set to 0 [fail] on call +* for probe to take place on individual page). * -- return = FALSE if not supported by underlying hardware, TRUE if supported. */ -BOOL DeviceProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Out_ __bcount(cPages) PBYTE pbResultMap); +BOOL DeviceProbeDMA(_Inout_ PPCILEECH_CONTEXT ctx, _In_ QWORD qwAddr, _In_ DWORD cPages, _Inout_ __bcount(cPages) PBYTE pbResultMap); /* * Write target physical memory. If an KMD is inserted in the target kernel the diff --git a/pcileech/device605_601.c b/pcileech/device605_601.c deleted file mode 100644 index 440c61e..0000000 --- a/pcileech/device605_601.c +++ /dev/null @@ -1,517 +0,0 @@ -// 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_TLP_TX_VALID 0x77000000 -#define FPGA_TLP_TX_VALID_LAST 0x77040000 -#define FPGA_LOOP_TX_VALID 0x77020000 - -#define SP605_601_PROBE_MAXPAGES 0x400 -#define SP605_601_MAX_SIZE_RX 0x0001f000 // in data bytes (excl. overhead/TLP headers) -#define SP605_601_MAX_SIZE_TX 0x00002000 // in total data (incl. overhead/TLP headers) - -#define FPGA_CMD_VERSION 0x01 -#define FPGA_CMD_PCIE_STATUS 0x02 - -#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)) - -// Delay in uS. DELAY_READ=300, DELAY_WRITE=150 -> 85MB/s. -// Values below are a bit more conservative for hw tolerance reasons. -#define DELAY_READ_DEFAULT 400 -#define DELAY_WRITE_DEFAULT 175 -#define DELAY_PROBE_DEFAULT 500 - -typedef struct tdDEVICE_CONTEXT_SP605_601 { - WORD wDeviceId; - WORD wFpgaVersion; - WORD wFpgaStatus; - 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); - DWORD DELAY_READ; - DWORD DELAY_WRITE; - DWORD DELAY_PROBE; -} 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, j; - PBYTE pbRX = LocalAlloc(0, 0x01000000); - DWORD dwStatus, dwData, cdwCfg = 0; - PDWORD pdwData; - BYTE pbTX[24] = { - // cfg read addr 0 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x77, - // cmd msg: version - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x77, - // cmd msg: PCIe status - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 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) { goto fail; } - for(i = 0; i < cbRX; i += 32) { - dwStatus = *(PDWORD)(pbRX + i); - pdwData = (PDWORD)(pbRX + i + 4); - if((dwStatus & 0xf0000000) != 0xe0000000) { continue; } - for(j = 0; j < 7; j++) { - dwData = *pdwData; - if((dwStatus & 0x03) == 0x03) { // CMD REPLY (or filler) - if((dwData >> 24) == FPGA_CMD_VERSION) { // FPGA bitstream version - ctx->wFpgaVersion = (WORD)dwData; - } - if((dwData >> 24) == FPGA_CMD_PCIE_STATUS) { // PCIe status - ctx->wFpgaStatus = (WORD)dwData; - } - } - if((dwStatus & 0x03) == 0x01) { // PCIe CFG REPLY - if(((++cdwCfg % 2) == 0) && (WORD)dwData) { // DeviceID - ctx->wDeviceId = (WORD)dwData; - } - } - pdwData++; - dwStatus >>= 4; - } - } -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 > SP605_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 i, j, cdwTlp = 0; - BYTE pbTlp[TLP_RX_MAX_SIZE]; - PDWORD pdwTlp = (PDWORD)pbTlp; - PDWORD pdwRx = (PDWORD)ctx->rxbuf.pb; - DWORD dwStatus, *pdwData; - 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; - } - for(i = 0; i < ctx->rxbuf.cb; i += 32) { // index in 64-bit (QWORD) - dwStatus = *(PDWORD)(ctx->rxbuf.pb + i); - pdwData = (PDWORD)(ctx->rxbuf.pb + i + 4); - if((dwStatus & 0xf0000000) != 0xe0000000) { continue; } - for(j = 0; j < 7; j++) { - if((dwStatus & 0x03) == 0x00) { // PCIe TLP - pdwTlp[cdwTlp] = *pdwData; - cdwTlp++; - if(cdwTlp >= TLP_RX_MAX_SIZE / sizeof(DWORD)) { return; } - } - if((dwStatus & 0x07) == 0x04) { // PCIe TLP and LAST - 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; - } - pdwData++; - dwStatus >>= 4; - } - } -} - -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, isFlush; - PTLP_HDR_MRdWr64 hdrRd64 = (PTLP_HDR_MRdWr64)tx; - PTLP_HDR_MRdWr32 hdrRd32 = (PTLP_HDR_MRdWr32)tx; - if(cb > SP605_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]); - } - isFlush = ((o % 0x8000) == 0x7000); - if(isFlush) { - Device605_601_TxTlp(ctx, (PBYTE)tx, is32 ? 12 : 16, FALSE, TRUE); - usleep(ctx->DELAY_WRITE); - } else { - Device605_601_TxTlp(ctx, (PBYTE)tx, is32 ? 12 : 16, FALSE, FALSE); - } - } - Device605_601_TxTlp(ctx, NULL, 0, TRUE, TRUE); - usleep(ctx->DELAY_READ); - 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(ctx->DELAY_PROBE); - 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)(1.25 * SP605_601_MAX_SIZE_RX + 0x1000); // 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 = SP605_601_MAX_SIZE_TX + 0x10000; - ctx->txbuf.pb = LocalAlloc(0, ctx->txbuf.cbMax); - if(!ctx->txbuf.pb) { goto fail; } - ctx->isPrintTlp = ctxPcileech->cfg->fVerboseExtra; - ctx->DELAY_READ = ctxPcileech->cfg->qwDeviceOpt[0] ? (DWORD)ctxPcileech->cfg->qwDeviceOpt[0] : DELAY_READ_DEFAULT; - ctx->DELAY_WRITE = ctxPcileech->cfg->qwDeviceOpt[1] ? (DWORD)ctxPcileech->cfg->qwDeviceOpt[1] : DELAY_WRITE_DEFAULT; - ctx->DELAY_PROBE = ctxPcileech->cfg->qwDeviceOpt[2] ? (DWORD)ctxPcileech->cfg->qwDeviceOpt[2] : DELAY_PROBE_DEFAULT; - // set callback functions and fix up config - ctxPcileech->cfg->dev.tp = PCILEECH_DEVICE_SP605_FT601; - ctxPcileech->cfg->dev.qwMaxSizeDmaIo = SP605_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) { - if((ctx->DELAY_READ != DELAY_READ_DEFAULT) || (ctx->DELAY_WRITE != DELAY_WRITE_DEFAULT) || (ctx->DELAY_PROBE != DELAY_PROBE_DEFAULT)) { - printf("Device Info: SP605 / FT601 [%i,%i,%i]\n", ctx->DELAY_READ, ctx->DELAY_WRITE, ctx->DELAY_PROBE); - } else { - 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_FT601) { - 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 deleted file mode 100644 index cf730be..0000000 --- a/pcileech/device605_601.h +++ /dev/null @@ -1,17 +0,0 @@ -// 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/devicefpga.c b/pcileech/devicefpga.c new file mode 100644 index 0000000..b635325 --- /dev/null +++ b/pcileech/devicefpga.c @@ -0,0 +1,835 @@ +// devicefpga.h : implementation related to the: +// - Xilinx SP605 dev board flashed with PCILeech bitstream and FTDI UMFT601X-B addon-board. +// - Xilinx AC701 dev board flashed with PCILeech bitstream and FTDI UMFT601X-B addon-board. +// - PCIeScreamer board flashed with PCILeech bitstream. +// +// (c) Ulf Frisk, 2017-2018 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifdef WIN32 + +#include "devicefpga.h" +#include "device.h" +#include "tlp.h" +#include "util.h" + +//------------------------------------------------------------------------------- +// FPGA defines below. +//------------------------------------------------------------------------------- + +#define FPGA_CMD_VERSION 0x01 +#define FPGA_CMD_ID 0x03 + +#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 tdDEV_CFG_PHY { + BYTE magic; + BYTE tp_cfg : 4; + BYTE tp : 4; + struct { + BYTE pl_directed_link_auton : 1; + BYTE pl_directed_link_change : 2; + BYTE pl_directed_link_speed : 1; + BYTE pl_directed_link_width : 2; + BYTE pl_upstream_prefer_deemph : 1; + BYTE pl_transmit_hot_rst : 1; + BYTE pl_downstream_deemph_source : 1; + BYTE _filler : 7; + } wr; + struct { + BYTE pl_ltssm_state : 6; + BYTE pl_rx_pm_state : 2; + BYTE pl_tx_pm_state : 3; + BYTE pl_initial_link_width : 3; + BYTE pl_lane_reversal_mode : 2; + BYTE pl_sel_lnk_width : 2; + BYTE pl_phy_lnk_up : 1; + BYTE pl_link_gen2_cap : 1; + BYTE pl_link_partner_gen2_supported : 1; + BYTE pl_link_upcfg_cap : 1; + BYTE pl_sel_lnk_rate : 1; + BYTE pl_directed_change_done : 1; + BYTE pl_received_hot_rst : 1; + BYTE _filler : 7; + } rd; +} DEV_CFG_PHY, *PDEV_CFG_PHY; + +typedef struct tdDEVICE_PERFORMANCE { + LPSTR SZ_DEVICE_NAME; + DWORD PROBE_MAXPAGES; // 0x400 + DWORD RX_FLUSH_LIMIT; + DWORD MAX_SIZE_RX; // in data bytes (excl. overhead/TLP headers) + DWORD MAX_SIZE_TX; // in total data (incl. overhead/TLP headers) + DWORD DELAY_PROBE_READ; + DWORD DELAY_PROBE_WRITE; + DWORD DELAY_WRITE; + DWORD DELAY_READ; + BOOL RETRY_ON_ERROR; +} DEVICE_PERFORMANCE, *PDEVICE_PERFORMANCE; + +#define DEVICE_ID_SP605_FT601 0 +#define DEVICE_ID_PCIESCREAMER 1 +#define DEVICE_ID_AC701_FT601 2 + +#define PERFORMANCE_PROFILE_SP605_FT601 0 +#define PERFORMANCE_PROFILE_PCIESCREAMER 1 +#define PERFORMANCE_PROFILE_AC701_FT601 2 +#define PERFORMANCE_PROFILE_MAX 2 + +const DEVICE_PERFORMANCE PERFORMANCE_PROFILES[PERFORMANCE_PROFILE_MAX + 1] = { + { + .SZ_DEVICE_NAME = "SP605 / FT601", + .PROBE_MAXPAGES = 0x400, + .RX_FLUSH_LIMIT = 0x8000, + .MAX_SIZE_RX = 0x1f000, + .MAX_SIZE_TX = 0x2000, + .DELAY_PROBE_READ = 500, + .DELAY_PROBE_WRITE = 0, + .DELAY_WRITE = 175, + .DELAY_READ = 400, + .RETRY_ON_ERROR = FALSE + }, { + // The PCIeScreamer or at least the current bitstream implementation running + // on it have a problem with the PCIe link stability which results on lost or + // delayed TLPS - workarounds are in place to retry after a delay. + .SZ_DEVICE_NAME = "PCIeScreamer", + .PROBE_MAXPAGES = 0x400, + .RX_FLUSH_LIMIT = 0xfffff000, + .MAX_SIZE_RX = 0x1c000, + .MAX_SIZE_TX = 0x1000, + .DELAY_PROBE_READ = 1000, + .DELAY_PROBE_WRITE = 150, + .DELAY_WRITE = 0, + .DELAY_READ = 500, + .RETRY_ON_ERROR = TRUE + }, { + .SZ_DEVICE_NAME = "AC701 / FT601", + .PROBE_MAXPAGES = 0x400, + .RX_FLUSH_LIMIT = 0xfffff000, + .MAX_SIZE_RX = 0x24000, + .MAX_SIZE_TX = 0x8000, + .DELAY_PROBE_READ = 500, + .DELAY_PROBE_WRITE = 0, + .DELAY_WRITE = 0, + .DELAY_READ = 200, + .RETRY_ON_ERROR = FALSE + } +}; + +typedef struct tdDEVICE_CONTEXT_FPGA { + WORD wDeviceId; + WORD wFpgaVersion; + WORD wFpgaID; + BOOL phySupported; + DEV_CFG_PHY phy; + DEVICE_PERFORMANCE perf; + 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); +} DEVICE_CONTEXT_FPGA, *PDEVICE_CONTEXT_FPGA; + +// STRUCT FROM FTD3XX.h +typedef struct { + USHORT VendorID; + USHORT ProductID; + UCHAR StringDescriptors[128]; + UCHAR Reserved; + UCHAR PowerAttributes; + USHORT PowerConsumption; + UCHAR Reserved2; + UCHAR FIFOClock; + UCHAR FIFOMode; + UCHAR ChannelConfig; + USHORT OptionalFeatureSupport; + UCHAR BatteryChargingGPIOConfig; + UCHAR FlashEEPROMDetection; + ULONG MSIO_Control; + ULONG GPIO_Control; +} FT_60XCONFIGURATION, *PFT_60XCONFIGURATION; + +//------------------------------------------------------------------------------- +// FPGA implementation below: +//------------------------------------------------------------------------------- + +LPSTR DeviceFPGA_InitializeFTDI(_In_ PDEVICE_CONTEXT_FPGA ctx) +{ + LPSTR szErrorReason; + CHAR c; + DWORD status; + ULONG(*pfnFT_GetChipConfiguration)(HANDLE ftHandle, PVOID pvConfiguration); + ULONG(*pfnFT_SetChipConfiguration)(HANDLE ftHandle, PVOID pvConfiguration); + FT_60XCONFIGURATION oCfgNew, oCfgOld; + // Load FTDI Library + ctx->dev.hModule = LoadLibrary(L"FTD3XX.dll"); + if(!ctx->dev.hModule) { + szErrorReason = "Unable to load FTD3XX.dll"; + goto fail; + } + ctx->dev.pfnFT_AbortPipe = (ULONG(*)(HANDLE, UCHAR)) + GetProcAddress(ctx->dev.hModule, "FT_AbortPipe"); + ctx->dev.pfnFT_Create = (ULONG(*)(PVOID, DWORD, HANDLE*)) + GetProcAddress(ctx->dev.hModule, "FT_Create"); + ctx->dev.pfnFT_Close = (ULONG(*)(HANDLE)) + GetProcAddress(ctx->dev.hModule, "FT_Close"); + 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"); + pfnFT_GetChipConfiguration = (ULONG(*)(HANDLE, PVOID))GetProcAddress(ctx->dev.hModule, "FT_GetChipConfiguration"); + pfnFT_SetChipConfiguration = (ULONG(*)(HANDLE, PVOID))GetProcAddress(ctx->dev.hModule, "FT_SetChipConfiguration"); + if(!ctx->dev.pfnFT_Create) { + szErrorReason = "Unable to retrieve required functions from FTD3XX.dll"; + goto fail; + } + // Open FTDI + status = ctx->dev.pfnFT_Create(NULL, 0x10 /*FT_OPEN_BY_INDEX*/, &ctx->dev.hFTDI); + if(status || !ctx->dev.hFTDI) { + szErrorReason = "Unable to connect to USB/FT601 device"; + goto fail; + } + ctx->dev.pfnFT_AbortPipe(ctx->dev.hFTDI, 0x02); + ctx->dev.pfnFT_AbortPipe(ctx->dev.hFTDI, 0x82); + // Check FTDI chip configuration and update if required + status = pfnFT_GetChipConfiguration(ctx->dev.hFTDI, &oCfgOld); + if(status) { + szErrorReason = "Unable to retrieve device configuration"; + goto fail; + } + memcpy(&oCfgNew, &oCfgOld, sizeof(FT_60XCONFIGURATION)); + oCfgNew.FIFOMode = 0; // FIFO MODE FT245 + oCfgNew.ChannelConfig = 2; // 1 CHANNEL ONLY + oCfgNew.OptionalFeatureSupport = 0; + if(memcmp(&oCfgNew, &oCfgOld, sizeof(FT_60XCONFIGURATION))) { + printf( + "IMPORTANT NOTE! FTDI FT601 USB CONFIGURATION DIFFERS FROM RECOMMENDED\n" \ + "PLEASE ENSURE THAT ONLY PCILEECH FPGA FTDI FT601 DEVICE IS CONNECED \n" \ + "BEFORE UPDATING CONFIGURATION. DO YOU WISH TO CONTINUE Y/N? \n" + ); + while(TRUE) { + c = (CHAR)getchar(); + if(c == 'Y' || c == 'y') { break; } + if(c == 'N' || c == 'n') { + szErrorReason = "User abort required device configuration"; + goto fail; + } + + } + status = pfnFT_SetChipConfiguration(ctx->dev.hFTDI, &oCfgNew); + if(status) { + szErrorReason = "Unable to set required device configuration"; + goto fail; + } + printf("FTDI USB CONFIGURATION UPDATED - RESETTING AND CONTINUING ...\n"); + ctx->dev.pfnFT_Close(ctx->dev.hFTDI); + FreeLibrary(ctx->dev.hModule); + ctx->dev.hModule = NULL; + ctx->dev.hFTDI = NULL; + Sleep(3000); + return DeviceFPGA_InitializeFTDI(ctx); + } + return NULL; +fail: + if(ctx->dev.hFTDI && ctx->dev.pfnFT_Close) { ctx->dev.pfnFT_Close(ctx->dev.hFTDI); } + if(ctx->dev.hModule) { FreeLibrary(ctx->dev.hModule); } + ctx->dev.hModule = NULL; + ctx->dev.hFTDI = NULL; + return szErrorReason; +} + +VOID DeviceFPGA_ReInitializeFTDI(_In_ PDEVICE_CONTEXT_FPGA ctx) +{ + // called to try to recover link in case of instable devices. + ctx->dev.pfnFT_Close(ctx->dev.hFTDI); + ctx->dev.hFTDI = NULL; + Sleep(250); + ctx->dev.pfnFT_Create(NULL, 0x10 /*FT_OPEN_BY_INDEX*/, &ctx->dev.hFTDI); +} + +VOID DeviceFPGA_Close(_Inout_ PPCILEECH_CONTEXT ctxPcileech) +{ + PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)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; +} + +BOOL DeviceFPGA_GetSetPHY(_In_ PDEVICE_CONTEXT_FPGA ctx, _In_ BOOL isUpdate) +{ + DWORD status; + DWORD i, j, cbRxTx, dwStatus; + PDWORD pdwData; + BYTE pbRx[0x1000]; + BYTE pbTx[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // dummy: to be overwritten + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x77, // cmd msg: version (filler) + }; + if(isUpdate) { + ctx->phy.magic = 0x77; + ctx->phy.tp_cfg = 1; + ctx->phy.tp = 4; + *(PQWORD)pbTx = _byteswap_uint64(*(PQWORD)&ctx->phy); + status = ctx->dev.pfnFT_WritePipe(ctx->dev.hFTDI, 0x02, pbTx, 16, &cbRxTx, NULL); + if(status) { return FALSE; } + Sleep(10); + } + *(PQWORD)&ctx->phy = 0; + *(PQWORD)pbTx = 0x7731000000000000; // phy read (3) + cfg (1) + magic (77) + status = ctx->dev.pfnFT_WritePipe(ctx->dev.hFTDI, 0x02, pbTx, 16, &cbRxTx, NULL); + if(status) { return FALSE; } + Sleep(10); + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbRx, 0x1000, &cbRxTx, NULL); + if(status) { return FALSE; } + for(i = 0; i < cbRxTx; i += 32) { + while(*(PDWORD)(pbRx + i) == 0x55556666) { // skip over ftdi workaround dummy fillers + i += 4; + if(i > cbRxTx - 32) { return FALSE; } + } + dwStatus = *(PDWORD)(pbRx + i); + pdwData = (PDWORD)(pbRx + i + 4); + if((dwStatus & 0xf0000000) != 0xe0000000) { continue; } + for(j = 0; j < 7; j++) { + if(((dwStatus & 0x03) == 0x01) && ((*pdwData & 0xffff0000) == 0x77310000)) { // PCIe CFG REPLY + // sloppy algorithm below, but it works unless high amount of interfering incoming TLPs + *(PQWORD)(&ctx->phy) = _byteswap_uint64(*(PQWORD)(pdwData - 1)); + return TRUE; + } + pdwData++; + dwStatus >>= 4; + } + } + return FALSE; +} + +BYTE DeviceFPGA_PHY_GetLinkWidth(_In_ PDEVICE_CONTEXT_FPGA ctx) { + const BYTE LINK_WIDTH[4] = { 1, 2, 4, 8 }; + return LINK_WIDTH[ctx->phy.rd.pl_sel_lnk_width]; +} + +BYTE DeviceFPGA_PHY_GetPCIeGen(_In_ PDEVICE_CONTEXT_FPGA ctx) +{ + return 1 + ctx->phy.rd.pl_sel_lnk_rate; +} + +VOID DeviceFPGA_SetSpeedPCIeGen1(_In_ PDEVICE_CONTEXT_FPGA ctx) +{ + if(ctx->phySupported && ctx->phy.rd.pl_sel_lnk_rate) { + ctx->phy.wr.pl_directed_link_auton = 1; + ctx->phy.wr.pl_directed_link_speed = 0; + ctx->phy.wr.pl_directed_link_change = 2; + DeviceFPGA_GetSetPHY(ctx, TRUE); + } +} + +VOID DeviceFPGA_GetDeviceID_FpgaVersion(_In_ PDEVICE_CONTEXT_FPGA ctx) +{ + DWORD status; + DWORD cbTX, cbRX, i, j; + PBYTE pbRX = LocalAlloc(0, 0x01000000); + DWORD dwStatus, dwData, cdwCfg = 0; + PDWORD pdwData; + BYTE pbTX[24] = { + // cfg status: (pcie bus,dev,fn id) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x77, + // cmd msg: FPGA bitstream version + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x77, + // cmd msg: FPGA bitstream device id + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x77 + }; + status = ctx->dev.pfnFT_WritePipe(ctx->dev.hFTDI, 0x02, pbTX, 24, &cbTX, NULL); + if(status) { goto fail; } + Sleep(10); + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbRX, 0x01000000, &cbRX, NULL); + if(status) { goto fail; } + for(i = 0; i < cbRX; i += 32) { + while(*(PDWORD)(pbRX + i) == 0x55556666) { // skip over ftdi workaround dummy fillers + i += 4; + if(i > cbRX - 32) { goto fail; } + } + dwStatus = *(PDWORD)(pbRX + i); + pdwData = (PDWORD)(pbRX + i + 4); + if((dwStatus & 0xf0000000) != 0xe0000000) { continue; } + for(j = 0; j < 7; j++) { + dwData = *pdwData; + if((dwStatus & 0x03) == 0x03) { // CMD REPLY (or filler) + if((dwData >> 24) == FPGA_CMD_VERSION) { // FPGA bitstream version + ctx->wFpgaVersion = (WORD)dwData; + } + if((dwData >> 24) == FPGA_CMD_ID) { // FPGA bitstream device id + ctx->wFpgaID = (WORD)dwData; + } + } + if((dwStatus & 0x03) == 0x01) { // PCIe CFG REPLY + if(((++cdwCfg % 2) == 0) && (WORD)dwData) { // DeviceID: (pcie bus,dev,fn id) + ctx->wDeviceId = (WORD)dwData; + } + } + pdwData++; + dwStatus >>= 4; + } + } + ctx->phySupported = (ctx->wFpgaVersion >= 3) ? DeviceFPGA_GetSetPHY(ctx, FALSE) : FALSE; +fail: + LocalFree(pbRX); +} + +VOID DeviceFPGA_SetPerformanceProfile(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _Inout_ PDEVICE_CONTEXT_FPGA ctx) +{ + switch(ctx->wFpgaID) { + case DEVICE_ID_PCIESCREAMER: + memcpy(&ctx->perf, &PERFORMANCE_PROFILES[PERFORMANCE_PROFILE_PCIESCREAMER], sizeof(DEVICE_PERFORMANCE)); + break; + case DEVICE_ID_AC701_FT601: + memcpy(&ctx->perf, &PERFORMANCE_PROFILES[PERFORMANCE_PROFILE_AC701_FT601], sizeof(DEVICE_PERFORMANCE)); + break; + default: + case DEVICE_ID_SP605_FT601: + memcpy(&ctx->perf, &PERFORMANCE_PROFILES[PERFORMANCE_PROFILE_SP605_FT601], sizeof(DEVICE_PERFORMANCE)); + break; + } + ctx->perf.DELAY_READ = ctxPcileech->cfg->DeviceOpt[0].isValid ? (DWORD)ctxPcileech->cfg->DeviceOpt[0].qwValue : ctx->perf.DELAY_READ; + ctx->perf.DELAY_WRITE = ctxPcileech->cfg->DeviceOpt[1].isValid ? (DWORD)ctxPcileech->cfg->DeviceOpt[1].qwValue : ctx->perf.DELAY_WRITE; + ctx->perf.DELAY_PROBE_READ = ctxPcileech->cfg->DeviceOpt[2].isValid ? (DWORD)ctxPcileech->cfg->DeviceOpt[2].qwValue : ctx->perf.DELAY_PROBE_READ; +} + +//------------------------------------------------------------------------------- +// TLP handling functionality below: +//------------------------------------------------------------------------------- + +BOOL DeviceFPGA_TxTlp(_In_ PDEVICE_CONTEXT_FPGA 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)) = 0x77000000; // TX TLP + } + if(cbTlp) { + *(PDWORD)(pbTx + ((i << 1) - 4)) = 0x77040000; // TX TLP VALID LAST + } + if(fRdKeepalive) { + cbTx += 8; + *(PDWORD)(pbTx + (i << 1)) = 0xffeeddcc; + *(PDWORD)(pbTx + ((i << 1) + 4)) = 0x77020000; // LOOPBACK TX + } + ctx->txbuf.cb += cbTx; + // transmit + if((ctx->txbuf.cb > ctx->perf.MAX_SIZE_TX) || (fFlush && ctx->txbuf.cb)) { + status = ctx->dev.pfnFT_WritePipe(ctx->dev.hFTDI, 0x02, ctx->txbuf.pb, ctx->txbuf.cb, &cbTxed, NULL); + if(status == 0x20 && ctx->perf.RETRY_ON_ERROR) { + DeviceFPGA_ReInitializeFTDI(ctx); // try recovery if possible. + 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 2048 +VOID DeviceFPGA_RxTlpSynchronous(_In_ PDEVICE_CONTEXT_FPGA ctx) +{ + DWORD status; + DWORD i, j, cdwTlp = 0; + BYTE pbTlp[TLP_RX_MAX_SIZE]; + PDWORD pdwTlp = (PDWORD)pbTlp; + PDWORD pdwRx = (PDWORD)ctx->rxbuf.pb; + DWORD dwStatus, *pdwData; + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb, ctx->rxbuf.cbMax, &ctx->rxbuf.cb, NULL); + if(status == 0x20 && ctx->perf.RETRY_ON_ERROR) { + DeviceFPGA_ReInitializeFTDI(ctx); // try recovery if possible. + 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; + } + for(i = 0; i < ctx->rxbuf.cb; i += 32) { // index in 64-bit (QWORD) + while(*(PDWORD)(ctx->rxbuf.pb + i) == 0x55556666) { // skip over ftdi workaround dummy fillers + i += 4; + if(i > ctx->rxbuf.cb - 32) { return; } + } + dwStatus = *(PDWORD)(ctx->rxbuf.pb + i); + pdwData = (PDWORD)(ctx->rxbuf.pb + i + 4); + if((dwStatus & 0xf0000000) != 0xe0000000) { + continue; + } + for(j = 0; j < 7; j++) { + if((dwStatus & 0x03) == 0x00) { // PCIe TLP + pdwTlp[cdwTlp] = *pdwData; + cdwTlp++; + if(cdwTlp >= TLP_RX_MAX_SIZE / sizeof(DWORD)) { return; } + } + if((dwStatus & 0x07) == 0x04) { // PCIe TLP and LAST + 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; + } + pdwData++; + dwStatus >>= 4; + } + } +} + +BOOL DeviceFPGA_ReadDMA_Impl(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb) +{ + PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)ctxPcileech->hDevice; + TLP_CALLBACK_BUF_MRd rxbuf; + DWORD tx[4], o, i; + BOOL is32, isFlush; + PTLP_HDR_MRdWr64 hdrRd64 = (PTLP_HDR_MRdWr64)tx; + PTLP_HDR_MRdWr32 hdrRd32 = (PTLP_HDR_MRdWr32)tx; + if(cb > ctx->perf.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]); + } + isFlush = ((o % ctx->perf.RX_FLUSH_LIMIT) == (ctx->perf.RX_FLUSH_LIMIT - 0x1000)); + if(isFlush) { + DeviceFPGA_TxTlp(ctx, (PBYTE)tx, is32 ? 12 : 16, FALSE, TRUE); + usleep(ctx->perf.DELAY_WRITE); + } else { + DeviceFPGA_TxTlp(ctx, (PBYTE)tx, is32 ? 12 : 16, FALSE, FALSE); + } + } + DeviceFPGA_TxTlp(ctx, NULL, 0, TRUE, TRUE); + usleep(ctx->perf.DELAY_READ); + DeviceFPGA_RxTlpSynchronous(ctx); + ctx->pMRdBuffer = NULL; + return rxbuf.cb >= rxbuf.cbMax; +} + +BOOL DeviceFPGA_ReadDMA(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb) +{ + PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)ctxPcileech->hDevice; + BOOL result; + result = DeviceFPGA_ReadDMA_Impl(ctxPcileech, qwAddr, pb, cb); + if(!result && (cb <= 0x1000) && ctx->perf.RETRY_ON_ERROR) { + Sleep(100); + result = DeviceFPGA_ReadDMA_Impl(ctxPcileech, qwAddr, pb, cb); + } + return result; +} + +VOID DeviceFPGA_ProbeDMA_Impl(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ QWORD qwAddr, _In_ DWORD cPages, _Inout_ __bcount(cPages) PBYTE pbResultMap) +{ + DWORD i, j, cTxTlp = 0; + PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)ctxPcileech->hDevice; + TLP_CALLBACK_BUF_MRd bufMRd; + DWORD tx[4]; + BOOL is32, isFlush; + 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 > ctx->perf.PROBE_MAXPAGES) { + DeviceFPGA_ProbeDMA_Impl(ctxPcileech, qwAddr, ctx->perf.PROBE_MAXPAGES, pbResultMap); + cPages -= ctx->perf.PROBE_MAXPAGES; + pbResultMap += ctx->perf.PROBE_MAXPAGES; + qwAddr += ctx->perf.PROBE_MAXPAGES << 12; + } + // 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++) { + if(pbResultMap[i]) { continue; } // skip over if page already marked as ok + 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]); + } + isFlush = (++cTxTlp % 24 == 0); + if(isFlush) { + DeviceFPGA_TxTlp(ctx, (PBYTE)tx, is32 ? 12 : 16, FALSE, TRUE); + usleep(ctx->perf.DELAY_PROBE_WRITE); + } else { + DeviceFPGA_TxTlp(ctx, (PBYTE)tx, is32 ? 12 : 16, FALSE, FALSE); + } + } + DeviceFPGA_TxTlp(ctx, NULL, 0, TRUE, TRUE); + usleep(ctx->perf.DELAY_PROBE_READ); + DeviceFPGA_RxTlpSynchronous(ctx); + ctx->hRxTlpCallbackFn = NULL; + ctx->pMRdBuffer = NULL; +} + +VOID DeviceFPGA_ProbeDMA(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ QWORD qwAddr, _In_ DWORD cPages, _Inout_ __bcount(cPages) PBYTE pbResultMap) +{ + PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)ctxPcileech->hDevice; + DWORD i; + DeviceFPGA_ProbeDMA_Impl(ctxPcileech, qwAddr, cPages, pbResultMap); + if(ctx->perf.RETRY_ON_ERROR) { + for(i = 0; i < cPages; i++) { + if(0 == pbResultMap[i]) { + Sleep(100); + DeviceFPGA_ProbeDMA_Impl(ctxPcileech, qwAddr, cPages, pbResultMap); + return; + } + } + } +} + +// write max 128 byte packets. +BOOL DeviceFPGA_WriteDMA_TXP(_Inout_ PDEVICE_CONTEXT_FPGA 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 DeviceFPGA_TxTlp(ctx, pbTlp, cbTlp, FALSE, FALSE); +} + +BOOL DeviceFPGA_WriteDMA(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ QWORD qwA, _In_ PBYTE pb, _In_ DWORD cb) +{ + PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)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 = DeviceFPGA_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) ? + DeviceFPGA_WriteDMA_TXP(ctx, qwA, be, 0, pb, 4) : + DeviceFPGA_WriteDMA_TXP(ctx, qwA, 0xf, be, pb, cbtx); + pb += cbtx; + cb -= cbtx; + qwA += cbtx; + } + return DeviceFPGA_TxTlp(ctx, NULL, 0, FALSE, TRUE) && result; // Flush and Return. +} + +BOOL DeviceFPGA_ListenTlp(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ DWORD dwTime) +{ + PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)ctxPcileech->hDevice; + QWORD tmStart = GetTickCount64(); + while(GetTickCount64() - tmStart < dwTime) { + DeviceFPGA_TxTlp(ctx, NULL, 0, TRUE, TRUE); + Sleep(10); + DeviceFPGA_RxTlpSynchronous(ctx); + } + return TRUE; +} + +BOOL DeviceFPGA_WriteTlp(_Inout_ PPCILEECH_CONTEXT ctxPcileech, _In_ PBYTE pbTlp, _In_ DWORD cbTlp) +{ + PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)ctxPcileech->hDevice; + return DeviceFPGA_TxTlp(ctx, pbTlp, cbTlp, FALSE, TRUE); +} + +BOOL DeviceFPGA_Open(_Inout_ PPCILEECH_CONTEXT ctxPcileech) +{ + LPSTR szDeviceError; + PDEVICE_CONTEXT_FPGA ctx; + ctx = LocalAlloc(LMEM_ZEROINIT, sizeof(DEVICE_CONTEXT_FPGA)); + if(!ctx) { return FALSE; } + ctxPcileech->hDevice = (HANDLE)ctx; + szDeviceError = DeviceFPGA_InitializeFTDI(ctx); + if(szDeviceError) { goto fail; } + DeviceFPGA_GetDeviceID_FpgaVersion(ctx); + if(ctxPcileech->cfg->fForcePCIeGen1) { + DeviceFPGA_SetSpeedPCIeGen1(ctx); + DeviceFPGA_GetDeviceID_FpgaVersion(ctx); + } + if(!ctx->wDeviceId) { + szDeviceError = "Unable to retrieve required Device PCIe ID"; + goto fail; + } + DeviceFPGA_SetPerformanceProfile(ctxPcileech, ctx); + ctx->rxbuf.cbMax = (DWORD)(1.30 * ctx->perf.MAX_SIZE_RX + 0x1000); // 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 = ctx->perf.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_FPGA; + ctxPcileech->cfg->dev.qwMaxSizeDmaIo = ctx->perf.MAX_SIZE_RX; + ctxPcileech->cfg->dev.qwAddrMaxNative = 0x0000ffffffffffff; + ctxPcileech->cfg->dev.fPartialPageReadSupported = TRUE; + ctxPcileech->cfg->dev.pfnClose = DeviceFPGA_Close; + ctxPcileech->cfg->dev.pfnProbeDMA = ctx->perf.PROBE_MAXPAGES ? DeviceFPGA_ProbeDMA : NULL; + ctxPcileech->cfg->dev.pfnReadDMA = DeviceFPGA_ReadDMA; + ctxPcileech->cfg->dev.pfnWriteDMA = DeviceFPGA_WriteDMA; + ctxPcileech->cfg->dev.pfnWriteTlp = DeviceFPGA_WriteTlp; + ctxPcileech->cfg->dev.pfnListenTlp = DeviceFPGA_ListenTlp; + // return + if(ctxPcileech->cfg->fVerbose) { + printf("FPGA: Device Info: %s PCIe gen%i x%i [%i,%i,%i]\n", ctx->perf.SZ_DEVICE_NAME, DeviceFPGA_PHY_GetPCIeGen(ctx), DeviceFPGA_PHY_GetLinkWidth(ctx), ctx->perf.DELAY_READ, ctx->perf.DELAY_WRITE, ctx->perf.DELAY_PROBE_READ); + } + return TRUE; +fail: + if(szDeviceError && (ctxPcileech->cfg->fVerbose || (ctxPcileech->cfg->dev.tp == PCILEECH_DEVICE_FPGA))) { + printf("FPGA: ERROR: %s.\n", szDeviceError); + } + DeviceFPGA_Close(ctxPcileech); + return FALSE; +} + +#endif /* WIN32 */ +#if defined(LINUX) || defined(ANDROID) + +#include "devicefpga.h" + +BOOL DeviceFPGA_Open(_Inout_ PPCILEECH_CONTEXT ctx) +{ + if(ctx->cfg->dev.tp == PCILEECH_DEVICE_FPGA) { + printf("FPGA: Failed. FPGA device currently only supported in PCILeech for Windows."); + } + return FALSE; +} + +#endif /* LINUX || ANDROID */ diff --git a/pcileech/devicefpga.h b/pcileech/devicefpga.h new file mode 100644 index 0000000..0d9a05a --- /dev/null +++ b/pcileech/devicefpga.h @@ -0,0 +1,20 @@ +// devicefpga.h : definitions related to the: +// - Xilinx SP605 dev board flashed with PCILeech bitstream and FTDI UMFT601X-B addon-board. +// - Xilinx AC701 dev board flashed with PCILeech bitstream and FTDI UMFT601X-B addon-board. +// - PCIeScreamer board flashed with PCILeech bitstream. +// +// (c) Ulf Frisk, 2017-2018 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __DEVICEFPGA_H__ +#define __DEVICEFPGA_H__ +#include "pcileech.h" + +/* +* Open a connection to the PCILeech flashed FPGA device. +* -- ctx +* -- result +*/ +BOOL DeviceFPGA_Open(_Inout_ PPCILEECH_CONTEXT ctx); + +#endif /* __DEVICEFPGA_H__ */ diff --git a/pcileech/help.c b/pcileech/help.c index b71446e..8ef57d0 100644 --- a/pcileech/help.c +++ b/pcileech/help.c @@ -34,7 +34,8 @@ VOID Help_ShowGeneral() " loaded on program exit ( except for the kmdload command ). \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" \ + " DMA mode may access all memory if FPGA based hardware is used such as the: \n" \ + " SP605, AC701 and PCIeScreamer. \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" \ @@ -63,11 +64,10 @@ VOID Help_ShowGeneral() " mac_fvrecover2 DMA \n" \ " mac_disablevtd DMA \n" \ " Valid options: \n" \ - " -min : memory min address, valid range: 0x0..0xffffffffffffffff \n" \ + " -min : memory min address, valid range: 0x0 .. 0xffffffffffffffff \n" \ " default: 0x0 \n" \ - " -max : memory max address, valid range: 0x0..0xffffffffffffffff \n" \ - " default: (0xffffffff/4GB for USB3380) \n" \ - " default: actual memory size in KMD mode. \n" \ + " -max : memory max address, valid range: 0x0 .. 0xffffffffffffffff \n" \ + " default: (FPGA = no limit, USB3380 = 4GB) \n" \ " -out : name of output file. \n" \ " default: pcileech----