diff --git a/.gitignore b/.gitignore
index 2017cc5..18b32c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,5 +14,6 @@ leechrpc_c.c
leechrpc_s.c
/files/lib
/files/temp
+/files/ARM64
/files/x86/lib
/leechcorepyc/pkg_linux
diff --git a/LeechCore.sln b/LeechCore.sln
index 15b70cf..6100fc3 100644
--- a/LeechCore.sln
+++ b/LeechCore.sln
@@ -17,30 +17,40 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "leechagent", "leechagent\le
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
+ Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|ARM64.Build.0 = Debug|ARM64
{3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|x64.ActiveCfg = Debug|x64
{3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|x64.Build.0 = Debug|x64
{3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|x86.ActiveCfg = Debug|Win32
{3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|x86.Build.0 = Debug|Win32
+ {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|ARM64.ActiveCfg = Release|ARM64
+ {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|ARM64.Build.0 = Release|ARM64
{3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|x64.ActiveCfg = Release|x64
{3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|x64.Build.0 = Release|x64
{3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|x86.ActiveCfg = Release|Win32
{3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|x86.Build.0 = Release|Win32
+ {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Debug|ARM64.ActiveCfg = Debug|x64
{1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Debug|x64.ActiveCfg = Debug|x64
{1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Debug|x64.Build.0 = Debug|x64
{1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Debug|x86.ActiveCfg = Debug|Win32
+ {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Release|ARM64.ActiveCfg = Release|x64
{1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Release|x64.ActiveCfg = Release|x64
{1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Release|x64.Build.0 = Release|x64
{1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Release|x86.ActiveCfg = Release|Win32
+ {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|ARM64.ActiveCfg = Debug|x64
{931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|x64.ActiveCfg = Debug|x64
{931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|x64.Build.0 = Debug|x64
{931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|x86.ActiveCfg = Debug|Win32
{931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|x86.Build.0 = Debug|Win32
+ {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Release|ARM64.ActiveCfg = Release|x64
{931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Release|x64.ActiveCfg = Release|x64
{931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Release|x64.Build.0 = Release|x64
{931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Release|x86.ActiveCfg = Release|Win32
diff --git a/README.md b/README.md
index 2ceba45..66864b2 100644
--- a/README.md
+++ b/README.md
@@ -40,11 +40,11 @@ Please find a summary of the supported software based memory acquisition methods
Please find a summary of the supported hardware based memory acquisition methods listed below. All hardware based memory acquisition methods are supported on both Windows and Linux. The FPGA based methods however have a performance penalty on Linux and will max out at approx: 90MB/s compared to 150MB/s on Windows due to less optimized drivers.
| Device | Type | Interface | Speed | 64-bit memory access | PCIe TLP access | Plugin | Project
Sponsor |
| -------------------------------------------------------------------------------| ---- | --------- | ----- | -------------------- | --------------- | ------ | ------------------ |
-| [Screamer PCIe Squirrel](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/PCIeSquirrel) | USB-C | 150MB/s | Yes | Yes | No | 💖 |
-| [Enigma X1](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/EnigmaX1) | USB-C | 180MB/s | Yes | Yes | No | 💖 |
-| [PCIeScreamerR04](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ScreamerM2) | USB-C | 150MB/s | Yes | Yes | No | 💖 |
-| [ScreamerM2](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ScreamerM2) | USB3 | 150MB/s | Yes | Yes | No | 💖 |
-| [AC701/FT601](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ac701_ft601) | USB3 | 150MB/s | Yes | Yes | No | |
+| [Screamer PCIe Squirrel](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/PCIeSquirrel) | USB-C | 190MB/s | Yes | Yes | No | 💖 |
+| [Enigma X1](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/EnigmaX1) | USB-C | 200MB/s | Yes | Yes | No | 💖 |
+| [PCIeScreamerR04](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ScreamerM2) | USB-C | 190MB/s | Yes | Yes | No | 💖 |
+| [ScreamerM2](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ScreamerM2) | USB3 | 190MB/s | Yes | Yes | No | 💖 |
+| [AC701/FT601](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ac701_ft601) | USB3 | 190MB/s | Yes | Yes | No | |
| [PCIeScreamer](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/pciescreamer) | USB3 | 100MB/s | Yes | Yes | No | |
| [SP605/FT601](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/sp605_ft601) | USB3 | 75MB/s | Yes | Yes | No | |
| [Acorn/FT2232H](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/acorn_ft2232h)| USB2 | 25MB/s | Yes | Yes | No | |
@@ -203,3 +203,7 @@ v1.0-1.8
[v2.12](https://github.com/ufrisk/LeechCore/releases/tag/v2.12)
* Support for MemProcFS v5.
+
+[v2.13](https://github.com/ufrisk/LeechCore/releases/tag/v2.13)
+* FPGA performance improvements.
+* ARM64 Windows support.
diff --git a/includes/lib32/leechcore.lib b/includes/lib32/leechcore.lib
index cf587cc..ee94b87 100644
Binary files a/includes/lib32/leechcore.lib and b/includes/lib32/leechcore.lib differ
diff --git a/includes/lib64/leechcore.lib b/includes/lib64/leechcore.lib
index 77b15d1..0b746bc 100644
Binary files a/includes/lib64/leechcore.lib and b/includes/lib64/leechcore.lib differ
diff --git a/includes/libarm64/leechcore.lib b/includes/libarm64/leechcore.lib
new file mode 100644
index 0000000..c241f37
Binary files /dev/null and b/includes/libarm64/leechcore.lib differ
diff --git a/leechagent/version.h b/leechagent/version.h
index efebb42..e36b896 100644
--- a/leechagent/version.h
+++ b/leechagent/version.h
@@ -2,9 +2,9 @@
#define STRINGIZE(s) STRINGIZE2(s)
#define VERSION_MAJOR 2
-#define VERSION_MINOR 12
-#define VERSION_REVISION 1
-#define VERSION_BUILD 50
+#define VERSION_MINOR 13
+#define VERSION_REVISION 0
+#define VERSION_BUILD 51
#define VER_FILE_DESCRIPTION_STR "LeechAgent Memory Acquisition Service"
#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
diff --git a/leechcore/device_fpga.c b/leechcore/device_fpga.c
index af281a6..7ef7455 100644
--- a/leechcore/device_fpga.c
+++ b/leechcore/device_fpga.c
@@ -343,7 +343,7 @@ typedef struct tdTLP_CALLBACK_BUF_MRd_SCATTER {
_Success_(return)
BOOL TLP_ToString(_In_ PBYTE pbTlp, _In_ DWORD cbTlp, _Out_ LPSTR *pszTlpText, _Out_opt_ PDWORD pcbTlpText)
{
- DWORD i, iMax, cbHexAscii, cchHdr, cbResult;
+ DWORD i, iMax, cbHexAscii = 0, cchHdr, cbResult;
CHAR szHdr[MAX_PATH];
LPSTR szResult, tp = "";
DWORD hdrDwBuf[4];
@@ -1916,6 +1916,7 @@ VOID DeviceFPGA_RxTlpAsynchronous(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_F
if(status && (status != FT_IO_PENDING)) {
break;
}
+ fAsyncInProgress = TRUE;
}
// 2: process (partial) tlp result
while(oBuffer + 32 <= cbBuffer) {
@@ -1930,69 +1931,95 @@ VOID DeviceFPGA_RxTlpAsynchronous(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_F
if(cbBuffer > 0x00f00000) { break; }
if(prxbuf && (cbBytesToRead <= prxbuf->cbReadTotal)) { break; }
// 3: read overlapped
- status = fAsync ?
- ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE) :
- ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbBuffer + cbBuffer, cbReadMax, &cbRead, NULL);
+ if(fAsync) {
+ status = ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE);
+ fAsyncInProgress = TRUE;
+ } else {
+ status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbBuffer + cbBuffer, cbReadMax, &cbRead, NULL);
+ }
if(status) {
break;
}
}
+ if(fAsyncInProgress) {
+ // dummy clear overlapped result
+ ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE);
+ }
}
VOID DeviceFPGA_RxTlpSynchronous(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_FPGA ctx, _In_opt_ DWORD dwBytesToRead)
{
DWORD status;
- DWORD i, j, cdwTlp = 0, cbReadRxBuf;
+ BOOL fRetry = FALSE;
+ DWORD i = 0, j, cdwTlp = 0, cbReadRxBuf;
BYTE pbTlp[TLP_RX_MAX_SIZE];
PDWORD pdwTlp = (PDWORD)pbTlp;
- DWORD dwStatus, *pdwData;
+ DWORD dwStatus, *pdwData, cbRx;
// larger read buffer slows down FT_ReadPipe so set it fairly tight if possible.
+ ctx->rxbuf.cb = 0;
cbReadRxBuf = ctx->dev.f2232h ? ctx->rxbuf.cbMax :
min(ctx->rxbuf.cbMax, dwBytesToRead ? max(0x4000, (0x1000 + dwBytesToRead + (dwBytesToRead >> 1))) : (DWORD)-1);
- status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb, cbReadRxBuf, &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 32-bit (DWORD)
- while(*(PDWORD)(ctx->rxbuf.pb + i) == 0x55556666) { // skip over ftdi workaround dummy fillers
- i += 4;
- if(i + 32 > ctx->rxbuf.cb) { return; }
+ while(TRUE) {
+ // read data:
+ status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb + ctx->rxbuf.cb, cbReadRxBuf - ctx->rxbuf.cb, &cbRx, 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.cb, ctx->rxbuf.cbMax - ctx->rxbuf.cb, &cbRx, NULL);
}
- dwStatus = *(PDWORD)(ctx->rxbuf.pb + i);
- pdwData = (PDWORD)(ctx->rxbuf.pb + i + 4);
- if((dwStatus & 0xf0000000) != 0xe0000000) {
- continue;
+ if(status) {
+ ctx->dev.pfnFT_AbortPipe(ctx->dev.hFTDI, 0x82);
+ return;
}
- 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; }
+ ctx->rxbuf.cb += cbRx;
+ // process retrieved data and send to respective consumer functions.
+ while(i + 32 <= ctx->rxbuf.cb) { // index in 32-bit (DWORD)
+ if(*(PDWORD)(ctx->rxbuf.pb + i) == 0x55556666) { // skip over ftdi workaround dummy fillers
+ i += 4;
+ continue;
}
- if((dwStatus & 0x07) == 0x04) { // PCIe TLP and LAST
- if(cdwTlp >= 3) {
- if(ctxLC->fPrintf[LC_PRINTF_VVV]) {
- TLP_Print(ctxLC, pbTlp, cdwTlp << 2, FALSE);
- }
- if(ctx->read_callback.pfn) {
- DeviceFPGA_RxTlp_UserCallback(ctxLC, ctx, pbTlp, cdwTlp << 2);
- }
- if(ctx->hRxTlpCallbackFn) {
- ctx->hRxTlpCallbackFn(ctx->pMRdBufferX, pbTlp, cdwTlp << 2);
+ dwStatus = *(PDWORD)(ctx->rxbuf.pb + i);
+ pdwData = (PDWORD)(ctx->rxbuf.pb + i + 4);
+ if((dwStatus & 0xf0000000) != 0xe0000000) {
+ lcprintfvv(ctxLC, "Device Info: FPGA: Bad no-header data received! Should not happen!\n");
+ i += 4;
+ 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(ctxLC->fPrintf[LC_PRINTF_VVV]) {
+ TLP_Print(ctxLC, pbTlp, cdwTlp << 2, FALSE);
+ }
+ if(ctx->read_callback.pfn) {
+ DeviceFPGA_RxTlp_UserCallback(ctxLC, ctx, pbTlp, cdwTlp << 2);
+ }
+ if(ctx->hRxTlpCallbackFn) {
+ ctx->hRxTlpCallbackFn(ctx->pMRdBufferX, pbTlp, cdwTlp << 2);
+ }
+ } else {
+ lcprintf(ctxLC, "Device Info: FPGA: Bad PCIe TLP received! Should not happen!\n");
}
- } else {
- printf("Device Info: FPGA: Bad PCIe TLP received! Should not happen!\n");
+ cdwTlp = 0;
}
- cdwTlp = 0;
+ pdwData++;
+ dwStatus >>= 4;
}
- pdwData++;
- dwStatus >>= 4;
+ i += 8 * 4;
+ }
+ // return upon (successful) finish!
+ if((cdwTlp == 0) || fRetry || (ctx->rxbuf.cbMax - ctx->rxbuf.cb < 0x400)) {
+ return;
}
+ // read retry should be attempted (in case of partial tlp received at the end)
+ lcprintfvv(ctxLC, "Device Info: FPGA: Partial read - read retry attempted!\n");
+ cbReadRxBuf = min(ctx->rxbuf.cbMax, cbReadRxBuf + 0x1000);
+ usleep(min(20, ctx->perf.DELAY_READ));
+ fRetry = TRUE;
}
}
@@ -2119,6 +2146,284 @@ VOID DeviceFPGA_ReadScatter_Impl(_In_ PLC_CONTEXT ctxLC, _In_ DWORD cMEMs, _Inou
ctx->pMRdBufferX = NULL;
}
+
+
+//-------------------------------------------------------------------------------
+// TLP NEW ASYNC handling functionality below:
+//-------------------------------------------------------------------------------
+
+#define MEM_SCATTER_DO_PROCESS(pMEM) (!pMEM->f && pMEM->cb && !(pMEM->cb & 7) && ((pMEM->qwA & 0xfff) + pMEM->cb <= 0x1000) && !MEM_SCATTER_ADDR_ISINVALID(pMEM))
+
+#define FPGA_ASYNC_BUFFER_SIZE 0x10000
+
+typedef struct tdFPGA_ASYNC_MEM_INFO {
+ BOOL f;
+ BOOL fTiny;
+ WORD cb;
+ WORD cbOffset;
+ DWORD iMEM;
+} FPGA_ASYNC_MEM_INFO, *PFPGA_ASYNC_MEM_INFO;
+
+typedef struct tdFPGA_ASYNC_INFO {
+ WORD wReqId;
+ BYTE bEccBit;
+ BYTE iTag;
+ BOOL fTiny;
+ DWORD iMEM;
+ DWORD cTagAvail;
+ DWORD cbReadOngoing;
+ DWORD cMEMsCompleted;
+ FPGA_ASYNC_MEM_INFO tag[0x70];
+ PPMEM_SCATTER ppMEMs;
+} FPGA_ASYNC_INFO, *PFPGA_ASYNC_INFO;
+
+VOID DeviceFPGA_ReadScatter_NewAsync_Impl_Tx(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_FPGA ctx, _In_ DWORD cMEMs, _Inout_ PPMEM_SCATTER ppMEMs, _In_ PFPGA_ASYNC_INFO pai)
+{
+ WORD o, cb;
+ DWORD j, cbFlush = 0;
+ BOOL fTiny, f32, fTx = FALSE;
+ PFPGA_ASYNC_MEM_INFO pmi;
+ PMEM_SCATTER pMEM;
+ DWORD tx[4] = { 0 };
+ PTLP_HDR_MRdWr64 hdrRd64 = (PTLP_HDR_MRdWr64)tx;
+ PTLP_HDR_MRdWr32 hdrRd32 = (PTLP_HDR_MRdWr32)tx;
+ while(TRUE) {
+ if((pai->iMEM >= cMEMs) || (pai->cTagAvail < 32) || (pai->cbReadOngoing >= ctx->perf.MAX_SIZE_RX)) { break; }
+ pMEM = ppMEMs[pai->iMEM];
+ if(!MEM_SCATTER_DO_PROCESS(pMEM)) {
+ pai->iMEM++;
+ continue;
+ }
+ f32 = (pMEM->qwA < 0x100000000);
+ fTiny = ctx->fAlgorithmReadTiny || (pMEM->cb != 0x1000);
+
+
+ o = 0;
+ while(o < pMEM->cb) {
+ cb = (WORD)(fTiny ? min(0x80, pMEM->cb - o) : pMEM->cb);
+ while((pmi = &pai->tag[pai->iTag]) && pmi->f) {
+ pai->iTag = (pai->iTag == 0x6f) ? 0 : (pai->iTag + 1);
+ }
+ pmi->f = TRUE;
+ pmi->fTiny = fTiny;
+ pmi->cb = cb;
+ pmi->cbOffset = o;
+ pmi->iMEM = pai->iMEM;
+ if(f32) {
+ hdrRd32->h.TypeFmt = TLP_MRd32;
+ hdrRd32->h.Length = (WORD)((cb < 0x1000) ? cb >> 2 : 0);
+ hdrRd32->RequesterID = ctx->wDeviceId;
+ hdrRd32->Tag = pai->iTag + pai->bEccBit;
+ hdrRd32->FirstBE = 0xf;
+ hdrRd32->LastBE = 0xf;
+ hdrRd32->Address = (DWORD)(pMEM->qwA + o);
+ } else {
+ hdrRd64->h.TypeFmt = TLP_MRd64;
+ hdrRd64->h.Length = (WORD)((cb < 0x1000) ? cb >> 2 : 0);
+ hdrRd64->RequesterID = ctx->wDeviceId;
+ hdrRd64->Tag = pai->iTag + pai->bEccBit;
+ hdrRd64->FirstBE = 0xf;
+ hdrRd64->LastBE = 0xf;
+ hdrRd64->AddressHigh = (DWORD)(pMEM->qwA >> 32);
+ hdrRd64->AddressLow = (DWORD)(pMEM->qwA + o);
+ }
+ for(j = 0; j < 4; j++) {
+ ENDIAN_SWAP_DWORD(tx[j]);
+ }
+
+ cbFlush += cb;
+ if(ctx->perf.RX_FLUSH_LIMIT && (cbFlush >= (fTiny ? 0x1000 : ctx->perf.RX_FLUSH_LIMIT))) {
+ // flush is only used by the SP605.
+ DeviceFPGA_TxTlp(ctxLC, ctx, (PBYTE)tx, f32 ? 12 : 16, FALSE, TRUE);
+ usleep(ctx->perf.DELAY_WRITE);
+ cbFlush = 0;
+ } else {
+ DeviceFPGA_TxTlp(ctxLC, ctx, (PBYTE)tx, f32 ? 12 : 16, FALSE, FALSE);
+ }
+ o += cb;
+ pai->cTagAvail--;
+ pai->cbReadOngoing += cb;
+ pai->iTag = (pai->iTag == 0x6f) ? 0 : (pai->iTag + 1);
+ }
+ pai->iMEM++;
+ fTx = TRUE;
+ }
+ if(fTx) {
+ DeviceFPGA_TxTlp(ctxLC, ctx, NULL, 0, TRUE, TRUE);
+ }
+}
+
+/*
+* Generic callback function that may be used by TLP capable devices to aid the
+* collection of memory read completions. Receives single TLP packet.
+* -- pai
+* -- pb
+* -- cb
+*/
+VOID DeviceFPGA_ReadScatter_NewAsync_Impl_RxTlpCB(_Inout_ PFPGA_ASYNC_INFO pai, _In_ PBYTE pb, _In_ DWORD cb)
+{
+ PTLP_HDR_CplD hdrC = (PTLP_HDR_CplD)pb;
+ PTLP_HDR hdr = (PTLP_HDR)pb;
+ PDWORD buf = (PDWORD)pb;
+ DWORD o, c, iTag;
+ PMEM_SCATTER pMEM;
+ PFPGA_ASYNC_MEM_INFO pmi;
+ BOOL fTiny, fCompleted;
+ buf[0] = _byteswap_ulong(buf[0]);
+ buf[1] = _byteswap_ulong(buf[1]);
+ buf[2] = _byteswap_ulong(buf[2]);
+ if(cb < ((DWORD)hdr->Length << 2) + 12) { return; } // Insufficient length
+ if(hdrC->RequesterID != pai->wReqId) { return; } // RequesterID mismatch
+ if(pai->bEccBit != (hdrC->Tag & 0x80)) { return; } // ECC bit mismatch
+ iTag = hdrC->Tag & 0x7f;
+ if(iTag >= 0x70) { return; } // tag out of range
+ pmi = &pai->tag[iTag];
+ pMEM = pai->ppMEMs[pmi->iMEM];
+ if(!pmi->f) { return; } // non active tag
+ if((hdr->TypeFmt == TLP_Cpl) && hdrC->Status) {
+ pai->cbReadOngoing -= pmi->cb;
+ if(MEM_SCATTER_STACK_PEEK(pMEM, 1) != (DWORD)-1) {
+ MEM_SCATTER_STACK_SET(pMEM, 1, (DWORD)-1);
+ pai->cMEMsCompleted++;
+ }
+ ZeroMemory(pmi, sizeof(FPGA_ASYNC_MEM_INFO));
+ pai->cTagAvail++;
+ return;
+ }
+ if(hdr->TypeFmt == TLP_CplD) {
+ fTiny = pai->fTiny || (pMEM->cb != 0x1000);
+ if(fTiny) {
+ if(hdrC->ByteCount > pmi->cb) { return; }
+ o = pmi->cbOffset + pmi->cb - hdrC->ByteCount;
+ } else {
+ o = 0x1000 - (hdrC->ByteCount ? hdrC->ByteCount : 0x1000);
+ }
+ c = (DWORD)hdr->Length << 2;
+ if(o + c > pMEM->cb) { return; }
+ memcpy(pMEM->pb + o, pb + 12, c);
+ MEM_SCATTER_STACK_ADD(pMEM, 1, c);
+ pai->cbReadOngoing -= c;
+ fCompleted = (pMEM->cb == MEM_SCATTER_STACK_PEEK(pMEM, 1));
+ if(fCompleted) {
+ pai->cMEMsCompleted++;
+ }
+ if(fCompleted || fTiny) {
+ ZeroMemory(pmi, sizeof(FPGA_ASYNC_MEM_INFO));
+ pai->cTagAvail++;
+ return;
+ }
+ }
+}
+
+VOID DeviceFPGA_ReadScatter_NewAsync_Impl(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_FPGA ctx, _In_ DWORD cMEMs, _Inout_ PPMEM_SCATTER ppMEMs)
+{
+ PMEM_SCATTER pMEM;
+ BOOL fAsyncInProgress = FALSE, fAsync;
+ DWORD iMEM, status, cbRead, cbReadMax;
+ DWORD cbBytesToRead = 0, cEmptyRead = 0, cbBuffer = 0, oBuffer = 0;
+ PBYTE pbBuffer;
+ DWORD cdwTlpDataConsumed;
+ FPGA_ASYNC_INFO ai = { 0 };
+ // 0: split too large reads
+ if(cMEMs > 1024) {
+ for(iMEM = 0; iMEM < cMEMs; iMEM += 1024) {
+ DeviceFPGA_ReadScatter_NewAsync_Impl(ctxLC, ctx, min(1024, cMEMs - iMEM), ppMEMs + iMEM);
+ }
+ return;
+ }
+ // 1: init
+ ai.ppMEMs = ppMEMs;
+ ai.cTagAvail = 0x70;
+ ai.wReqId = ctx->wDeviceId;
+ ai.bEccBit = ctx->RxEccBit;
+ ai.fTiny = ctx->fAlgorithmReadTiny;
+ ctx->pMRdBufferX = &ai;
+ ctx->hRxTlpCallbackFn = (VOID(*)(PVOID, PBYTE, DWORD))DeviceFPGA_ReadScatter_NewAsync_Impl_RxTlpCB;
+ pbBuffer = ctx->rxbuf.pb;
+ // 2: pre-check MEMs and set fAsync (if large enough read).
+ for(iMEM = 0; iMEM < cMEMs; iMEM++) {
+ pMEM = ppMEMs[iMEM];
+ if(MEM_SCATTER_DO_PROCESS(pMEM)) {
+ cbBytesToRead += pMEM->cb;
+ } else {
+ ai.cMEMsCompleted++;
+ }
+ }
+ if(cMEMs == ai.cMEMsCompleted) {
+ return;
+ }
+ fAsync = !ctx->dev.f2232h && !ai.fTiny && (cbBytesToRead > 0x4000);
+ cbReadMax = min(FPGA_ASYNC_BUFFER_SIZE, max(0x1800, cbBytesToRead + (cbBytesToRead >> 1)));
+ // 3: clear potential lingering dirty TLPs/Cpls (if required)
+ // TODO: IMPLEMENT THIS
+ // 4: transmit initial data:
+ DeviceFPGA_ReadScatter_NewAsync_Impl_Tx(ctxLC, ctx, cMEMs, ppMEMs, &ai);
+ // 5: initial read
+ usleep(15);
+ status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb, cbReadMax, &cbRead, NULL);
+ if(status && (status != FT_IO_PENDING)) {
+ return;
+ }
+ // 6: do main read loop
+ while(TRUE) {
+ // 6.1: check no-read ops and update buffer pointer with previously read data
+ cbBuffer += cbRead;
+ if((cbRead == 0) || (cbRead == 0x14)) {
+ cEmptyRead++;
+ if(cEmptyRead >= 0x30) {
+ break;
+ }
+ }
+ // 6.2: submit async read (if target read is large enough to gain from it)
+ if(fAsync) {
+ status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbBuffer + cbBuffer, cbReadMax, &cbRead, &ctx->async.oOverlapped);
+ if(status && (status != FT_IO_PENDING)) {
+ break;
+ }
+ fAsyncInProgress = TRUE;
+ }
+ // 6.3: process (partial) tlp result
+ while(oBuffer + 32 <= cbBuffer) {
+ cdwTlpDataConsumed = DeviceFPGA_RxTlpAsynchronous_Tlp(ctxLC, ctx, (cbBuffer - oBuffer) >> 2, (PDWORD)(pbBuffer + oBuffer));
+ if(cdwTlpDataConsumed == (DWORD)-1) {
+ break;
+ }
+ oBuffer += cdwTlpDataConsumed << 2;
+ cEmptyRead = 0;
+ }
+ // 6.4: check exit criteria
+ if(cbBuffer >= 0x01000000 - cbReadMax) {
+ break;
+ }
+ if(cMEMs == ai.cMEMsCompleted) {
+ break;
+ }
+ // 6.5: transmit additional TLPs
+ DeviceFPGA_ReadScatter_NewAsync_Impl_Tx(ctxLC, ctx, cMEMs, ppMEMs, &ai);
+ // 6.6: read (overlapped) result
+ if(fAsync) {
+ status = ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE);
+ fAsyncInProgress = FALSE;
+ } else {
+ status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbBuffer + cbBuffer, cbReadMax, &cbRead, NULL);
+ }
+ if(status) {
+ break;
+ }
+ }
+ if(fAsyncInProgress) {
+ // clear any on-going overlapped reads
+ ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE);
+ }
+ ctx->RxEccBit = ctx->RxEccBit ? 0 : 0x80;
+}
+
+
+
+//-------------------------------------------------------------------------------
+// TLP handling (cont.) functionality below:
+//-------------------------------------------------------------------------------
+
VOID DeviceFPGA_ReadScatter(_In_ PLC_CONTEXT ctxLC, _In_ DWORD cMEMs, _Inout_ PPMEM_SCATTER ppMEMs)
{
PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)ctxLC->hDevice;
@@ -2134,7 +2439,11 @@ VOID DeviceFPGA_ReadScatter(_In_ PLC_CONTEXT ctxLC, _In_ DWORD cMEMs, _Inout_ PP
MEM_SCATTER_STACK_PUSH(pMEM, 0);
}
}
+ if(ctx->async.fEnabled) {
+ DeviceFPGA_ReadScatter_NewAsync_Impl(ctxLC, ctx, cMEMs, ppMEMs);
+ } else {
DeviceFPGA_ReadScatter_Impl(ctxLC, cMEMs, ppMEMs);
+ }
for(i = 0; i < cMEMs; i++) {
pMEM = ppMEMs[i];
if(!pMEM->f && MEM_SCATTER_ADDR_ISVALID(pMEM)) {
@@ -2229,18 +2538,25 @@ VOID DeviceFPGA_ProbeMEM(_In_ PLC_CONTEXT ctxLC, _In_ QWORD pa, _In_ DWORD cPage
_Success_(return) LINUX_NO_OPTIMIZE
BOOL DeviceFPGA_WriteMEM_TXP(_In_ PLC_CONTEXT ctxLC, _Inout_ PDEVICE_CONTEXT_FPGA ctx, _In_ QWORD pa, _In_ BYTE bFirstBE, _In_ BYTE bLastBE, _In_ PBYTE pb, _In_ DWORD cb)
{
- static BYTE bTag = 0;
+ static BYTE bTag = 0x70;
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;
+ if(bTag == 0x7f) {
+ bTag = 0xe0;
+ } else if(bTag == 0xff) {
+ bTag = 0x70;
+ } else {
+ bTag++;
+ }
memset(pbTlp, 0, 16);
if(pa < 0x100000000) {
hdrWr32->h.TypeFmt = TLP_MWr32;
hdrWr32->h.Length = (WORD)(cb + 3) >> 2;
hdrWr32->FirstBE = bFirstBE;
hdrWr32->LastBE = bLastBE;
- hdrWr32->Tag = bTag++;
+ hdrWr32->Tag = bTag;
hdrWr32->RequesterID = ctx->wDeviceId;
hdrWr32->Address = (DWORD)pa;
for(i = 0; i < 3; i++) {
@@ -2253,7 +2569,7 @@ BOOL DeviceFPGA_WriteMEM_TXP(_In_ PLC_CONTEXT ctxLC, _Inout_ PDEVICE_CONTEXT_FPG
hdrWr64->h.Length = (WORD)(cb + 3) >> 2;
hdrWr64->FirstBE = bFirstBE;
hdrWr64->LastBE = bLastBE;
- hdrWr64->Tag = bTag++;
+ hdrWr64->Tag = bTag;
hdrWr64->RequesterID = ctx->wDeviceId;
hdrWr64->AddressHigh = (DWORD)(pa >> 32);
hdrWr64->AddressLow = (DWORD)pa;
diff --git a/leechcore/leechcore.vcxproj b/leechcore/leechcore.vcxproj
index bd8e004..f92e8ae 100644
--- a/leechcore/leechcore.vcxproj
+++ b/leechcore/leechcore.vcxproj
@@ -1,6 +1,10 @@

+
+ Debug
+ ARM64
+
Debug
Win32
@@ -9,6 +13,10 @@
Debug
x64
+
+ Release
+ ARM64
+
Release
Win32
@@ -39,6 +47,13 @@
Unicode
false
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+ false
+
DynamicLibrary
false
@@ -56,6 +71,15 @@
false
false
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+ false
+ false
+
@@ -67,12 +91,18 @@
+
+
+
+
+
+
$(SolutionDir)files\
@@ -82,6 +112,10 @@
$(SolutionDir)files\$(PlatformShortName)\
$(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\
+
+ $(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\
+ $(SolutionDir)files\$(PlatformShortName)\
+
$(SolutionDir)files\
$(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\
@@ -90,6 +124,10 @@
$(SolutionDir)files\$(PlatformShortName)\
$(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\
+
+ $(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\
+ $(SolutionDir)files\$(PlatformShortName)\
+
Level3
@@ -139,6 +177,39 @@ copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y
if not exist "$(SolutionDir)\includes\lib32" mkdir "$(SolutionDir)\includes\lib32"
copy "$(OutDir)\lib\leechcore.lib" "$(SolutionDir)\includes\lib32\" /y
+copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y
+
+
+
+
+
+
+
+ Stub
+
+
+ None
+ /robust %(AdditionalOptions)
+
+
+
+
+ Level3
+ Disabled
+ true
+ true
+ CompileAsC
+
+
+ rpcrt4.lib;setupapi.lib;winusb.lib;ws2_32.lib;%(AdditionalDependencies)
+ $(OutDir)\lib\$(TargetName).pdb
+ Windows
+ $(OutDir)\lib\$(TargetName).lib
+
+
+ if not exist "$(SolutionDir)\includes\libarm64" mkdir "$(SolutionDir)\includes\libarm64"
+copy "$(OutDir)\lib\leechcore.lib" "$(SolutionDir)\includes\libarm64\" /y
+
copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y
@@ -172,6 +243,7 @@ copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y
Windows
$(OutDir)\lib\$(TargetName).lib
UseLinkTimeCodeGeneration
+ true
if not exist "$(SolutionDir)\includes\lib64" mkdir "$(SolutionDir)\includes\lib64"
@@ -213,6 +285,44 @@ copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y
if not exist "$(SolutionDir)\includes\lib32" mkdir "$(SolutionDir)\includes\lib32"
copy "$(OutDir)\lib\leechcore.lib" "$(SolutionDir)\includes\lib32\" /y
+copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y
+
+
+
+
+
+
+
+ Stub
+
+
+ None
+ /robust %(AdditionalOptions)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ true
+ CompileAsC
+
+
+ true
+ true
+ rpcrt4.lib;setupapi.lib;winusb.lib;ws2_32.lib;%(AdditionalDependencies)
+ $(OutDir)\lib\$(TargetName).pdb
+ Windows
+ $(OutDir)\lib\$(TargetName).lib
+ UseLinkTimeCodeGeneration
+
+
+ if not exist "$(SolutionDir)\includes\libarm64" mkdir "$(SolutionDir)\includes\libarm64"
+copy "$(OutDir)\lib\leechcore.lib" "$(SolutionDir)\includes\libarm64\" /y
+
copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y
diff --git a/leechcore/leechcore.vcxproj.user b/leechcore/leechcore.vcxproj.user
index 3e6d071..12cd692 100644
--- a/leechcore/leechcore.vcxproj.user
+++ b/leechcore/leechcore.vcxproj.user
@@ -6,4 +6,7 @@
WindowsRemoteDebugger
+
+ WindowsRemoteDebugger
+
\ No newline at end of file
diff --git a/leechcore/version.h b/leechcore/version.h
index 75a6c22..a5592a0 100644
--- a/leechcore/version.h
+++ b/leechcore/version.h
@@ -2,9 +2,9 @@
#define STRINGIZE(s) STRINGIZE2(s)
#define VERSION_MAJOR 2
-#define VERSION_MINOR 12
-#define VERSION_REVISION 1
-#define VERSION_BUILD 50
+#define VERSION_MINOR 13
+#define VERSION_REVISION 0
+#define VERSION_BUILD 51
#define VER_FILE_DESCRIPTION_STR "LeechCore Memory Acquisition Library"
#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
diff --git a/leechcorepyc/pkggen_linux.sh b/leechcorepyc/pkggen_linux.sh
index eb6ae81..ffce8b9 100644
--- a/leechcorepyc/pkggen_linux.sh
+++ b/leechcorepyc/pkggen_linux.sh
@@ -39,7 +39,7 @@ leechcorepyc = Extension(
setup(
name='leechcorepyc',
- version='2.12.1', # VERSION_END
+ version='2.13.0', # VERSION_END
description='LeechCore for Python',
long_description='LeechCore for Python : native extension for physical memory access',
url='https://github.com/ufrisk/LeechCore',
diff --git a/leechcorepyc/version.h b/leechcorepyc/version.h
index a704de0..b9c36be 100644
--- a/leechcorepyc/version.h
+++ b/leechcorepyc/version.h
@@ -2,9 +2,9 @@
#define STRINGIZE(s) STRINGIZE2(s)
#define VERSION_MAJOR 2
-#define VERSION_MINOR 12
-#define VERSION_REVISION 1
-#define VERSION_BUILD 50
+#define VERSION_MINOR 13
+#define VERSION_REVISION 0
+#define VERSION_BUILD 51
#define VER_FILE_DESCRIPTION_STR "LeechCore Memory Acquisition Library : Python API"
#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD