├── AsmTool ├── Linux │ ├── CMakeLists.txt │ └── AsmIOLinux.c ├── PCIAddress.cs ├── AsmRegisters.cs ├── AsmTool.csproj ├── AsmIOFactory.cs ├── PCIBar.cs ├── AsmMemory.cs ├── IAsmIO.cs ├── Prober.cs ├── Program.cs ├── app.manifest ├── LinuxAsmIO.cs ├── WindowsAsmIO.cs ├── AsmFirmware.cs └── AsmDevice.cs ├── cmake ├── DotnetImports.props.in ├── FindSevenZip.cmake └── FindDotnet.cmake ├── .ci ├── linux_x64 │ └── Dockerfile └── windows_x64 │ └── Dockerfile ├── .cirrus.yml ├── .editorconfig ├── CMakeLists.txt ├── AsmTool.sln └── README.md /AsmTool/Linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_library(PCI_LIBRARY pci REQUIRED) 2 | 3 | add_library(AsmIoLinux SHARED 4 | AsmIOLinux.c 5 | ) 6 | target_link_libraries(AsmIoLinux ${PCI_LIBRARY}) -------------------------------------------------------------------------------- /cmake/DotnetImports.props.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | ${_DN_OUTPUT_PATH}/ 4 | ${_DN_XPLAT_LIB_DIR}/ 5 | ${_DN_VERSION} 6 | ${_DN_CUSTOM_BUILDPROPS} 7 | 8 | -------------------------------------------------------------------------------- /.ci/linux_x64/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:8.0 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y build-essential gcc g++ cmake pkg-config p7zip-full libpci-dev \ 5 | && apt-get clean \ 6 | && rm -rf /var/lib/apt/lists/* \ 7 | && dotnet nuget locals all --clear 8 | # reserved for cache eviction: 0 9 | -------------------------------------------------------------------------------- /AsmTool/PCIAddress.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | namespace AsmTool 10 | { 11 | public struct PCIAddress 12 | { 13 | public uint Bus; 14 | public uint Device; 15 | public uint Function; 16 | } 17 | } -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | linux_x64_task: 2 | container: 3 | cpu: 2 4 | memory: 2G 5 | dockerfile: .ci/linux_x64/Dockerfile 6 | 7 | build_script: 8 | - cmake -B build 9 | - cmake --build build 10 | 11 | binaries_artifacts: 12 | path: build/AsmTool*.zip 13 | 14 | 15 | windows_x86_task: 16 | windows_container: 17 | cpu: 2 18 | memory: 2G 19 | dockerfile: .ci/windows_x64/Dockerfile 20 | os_version: 2019 21 | 22 | build_script: 23 | - cmake -B build 24 | - cmake --build build 25 | 26 | binaries_artifacts: 27 | path: build/AsmTool*.zip 28 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = crlf 4 | 5 | [*.xml] 6 | indent_style = space 7 | 8 | [*.cs] 9 | indent_style = tab 10 | indent_size = 4 11 | csharp_new_line_before_open_brace = types,anonymous_types 12 | csharp_indent_labels = no_change 13 | csharp_indent_case_contents = true 14 | csharp_indent_switch_labels = true 15 | csharp_new_line_before_catch = false 16 | csharp_new_line_before_else = false 17 | csharp_new_line_before_finally = false 18 | 19 | csharp_style_expression_bodied_properties = when_on_single_line:suggestion 20 | csharp_style_expression_bodied_accessors = when_on_single_line:suggestion -------------------------------------------------------------------------------- /.ci/windows_x64/Dockerfile: -------------------------------------------------------------------------------- 1 | # escape=` 2 | 3 | FROM cirrusci/windowsservercore:cmake-2022.06.23 4 | 5 | RUN powershell -Command "` 6 | choco install -y dotnet-8.0-sdk; ` 7 | REG ADD 'HKLM\SOFTWARE\WOW6432Node\Microsoft\NET Framework Setup\NDP\v3.5' /t REG_DWORD /v Install /d 1; ` 8 | REG ADD 'HKLM\SOFTWARE\WOW6432Node\Microsoft\NET Framework Setup\NDP\v3.5' /t REG_DWORD /v SP /d 1; ` 9 | REG ADD 'HKLM\SOFTWARE\WOW6432Node\Microsoft\NET Framework Setup\NDP\v3.5' /t REG_SZ /v Version /d 3.5.30729.4926; ` 10 | Remove-Item $env:TEMP\* -recurse; ` 11 | & $Env:ProgramFiles\dotnet\dotnet nuget locals all --clear; ` 12 | # reserved for cache eviction: 0 13 | " 14 | 15 | -------------------------------------------------------------------------------- /AsmTool/AsmRegisters.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | namespace AsmTool 10 | { 11 | /// 12 | /// byte0 of internal register 13 | /// 14 | public enum ASMIOCommand : uint { 15 | Read = 0x40, 16 | SectorErase = 0x30, 17 | SectorWrite = 0x1E, 18 | Write = 0x23, 19 | // write status register 20 | WriteSR = 0x4C 21 | } 22 | 23 | public enum ASMIOFunction : uint { 24 | Memory = 0x4, 25 | Flash = 0x10 26 | } 27 | } -------------------------------------------------------------------------------- /AsmTool/AsmTool.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | true 9 | 10 | 11 | 12 | 13 | x86 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AsmTool/AsmIOFactory.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | using System.Runtime.InteropServices; 11 | namespace AsmTool 12 | { 13 | public class AsmIOFactory 14 | { 15 | public static IAsmIO GetAsmIO() { 16 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { 17 | return new LinuxAsmIO(); 18 | } else if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { 19 | return new WindowsAsmIO(); 20 | } else { 21 | throw new NotSupportedException("This Operating System is currently not supported"); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /AsmTool/PCIBar.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace AsmTool 16 | { 17 | public enum MemoryType : byte 18 | { 19 | Base32 = 0, 20 | Base64 = 2 21 | } 22 | 23 | public class PCIBar 24 | { 25 | private readonly uint bar; 26 | public PCIBar(uint bar) { 27 | this.bar = bar; 28 | } 29 | 30 | public byte MemorySpace => (byte)(bar & 1); 31 | public MemoryType Type => (MemoryType)((bar >> 1) & 2); 32 | public bool IsPrefetchable => ((bar >> 3) & 1) == 1; 33 | public uint BaseAddress => bar >> 4; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.10 FATAL_ERROR ) 2 | list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake") 3 | find_package(Dotnet 2.0 REQUIRED) 4 | 5 | project(AsmTool NONE) 6 | if(WIN32) 7 | set(REKO_COMPILER "Visual Studio 17 2022") 8 | endif() 9 | 10 | set(target_platform "") 11 | if(WIN32) 12 | # required for asmdll interop 13 | set(target_platform PLATFORM x86) 14 | endif() 15 | 16 | ADD_DOTNET(${CMAKE_SOURCE_DIR}/AsmTool.sln 17 | ${target_platform} 18 | CONFIG ${CMAKE_BUILD_TYPE} 19 | TARGET_NAME AsmTool_sln 20 | ) 21 | 22 | if(UNIX) 23 | enable_language(C) 24 | add_subdirectory(AsmTool/Linux) 25 | add_dependencies(AsmTool_sln AsmIoLinux) 26 | endif() 27 | 28 | include(FindSevenZip) 29 | execute_process( 30 | COMMAND git rev-parse HEAD 31 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 32 | OUTPUT_VARIABLE head_sha 33 | ) 34 | string(SUBSTRING "${head_sha}" 0 10 build_shorthash) 35 | 36 | set(file_list "") 37 | list(APPEND file_list 38 | ${CMAKE_BINARY_DIR}/AsmTool/net8.0/* 39 | ) 40 | 41 | if(UNIX) 42 | list(APPEND file_list $) 43 | endif() 44 | 45 | add_custom_target(create_zip 46 | COMMENT "Compressing..." 47 | COMMAND ${SEVENZIP_BIN} a 48 | ${CMAKE_BINARY_DIR}/AsmTool-${build_shorthash}.zip 49 | ${file_list} 50 | ) 51 | add_dependencies(create_zip AsmTool_sln) 52 | 53 | 54 | add_custom_target(main ALL) 55 | add_dependencies(main create_zip) 56 | -------------------------------------------------------------------------------- /cmake/FindSevenZip.cmake: -------------------------------------------------------------------------------- 1 | # This file is part of the Spring engine (GPL v2 or later), see LICENSE.html 2 | 3 | # - Find 7zip 4 | # Find the native 7zip binary 5 | # 6 | # NOTE: We can not use 7ZIP* or 7zip* as var name, cause in CMake, 7 | # var names should not start with numbers. 8 | # 9 | # SEVENZIP_BIN - will be set to the 7zip executable (eg. 7z.exe) 10 | # SEVENZIP_FOUND - TRUE if 7zip was found 11 | 12 | INCLUDE(FindPackageHandleStandardArgs) 13 | 14 | IF (SEVENZIP_BIN) 15 | # Already in cache, be silent 16 | SET(SevenZip_FIND_QUIETLY TRUE) 17 | ENDIF (SEVENZIP_BIN) 18 | 19 | file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) 20 | set(_PROGRAMFILESX86 "PROGRAMFILES(x86)") 21 | file(TO_CMAKE_PATH "$ENV{${_PROGRAMFILESX86}}" _programfiles_x86) 22 | file(TO_CMAKE_PATH "$ENV{ProgramW6432}" _programw6432) 23 | 24 | # 7zr(.exe) only supports 7z archives, while 7z(.exe) and 7za(.exe) 25 | # additionally support many other formats (eg zip) 26 | find_program(SEVENZIP_BIN 27 | NAMES 7z 7za 28 | HINTS "${MINGWDIR}" "${MINGWLIBS}/bin" "${_programfiles}/7-zip" "${_programfiles_x86}/7-zip" "${_programw6432}/7-zip" 29 | PATH_SUFFIXES bin 30 | DOC "7zip executable" 31 | ) 32 | # handle the QUIETLY and REQUIRED arguments and set SEVENZIP_FOUND to TRUE if 33 | # all listed variables are TRUE 34 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SevenZip DEFAULT_MSG SEVENZIP_BIN) 35 | 36 | MARK_AS_ADVANCED(SEVENZIP_BIN) 37 | -------------------------------------------------------------------------------- /AsmTool/AsmMemory.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Runtime.InteropServices; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | namespace AsmTool 17 | { 18 | public class AsmMemory : IDisposable 19 | { 20 | private readonly UInt32 handle; 21 | private readonly uint address; 22 | private readonly uint size; 23 | 24 | private readonly IAsmIO io; 25 | 26 | public AsmMemory(IAsmIO io, uint address, uint size) { 27 | this.io = io; 28 | 29 | this.address = address; 30 | this.size = size; 31 | this.handle = io.MapAsmIO(address, size); 32 | } 33 | 34 | public unsafe byte[] Read(uint offset, uint size) { 35 | byte[] buf = new byte[(int)size]; 36 | fixed(byte *bufPtr = buf) { 37 | // asmedia reads 1 byte at a time, replicate for safety 38 | for (uint i = 0; i < size; i++) { 39 | Read(bufPtr + i, offset + i, 1); 40 | } 41 | } 42 | return buf; 43 | } 44 | 45 | private unsafe void Read(byte *ptr, uint offset, uint size) { 46 | io.ReadMEM(handle + offset, size, new IntPtr(ptr)); 47 | } 48 | 49 | public void Dispose() { 50 | io.UnmapAsmIO(address, size); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /AsmTool.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33122.133 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsmTool", "AsmTool\AsmTool.csproj", "{AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x86 = Debug|x86 12 | Release|Any CPU = Release|Any CPU 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}.Debug|x86.ActiveCfg = Debug|Any CPU 19 | {AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}.Debug|x86.Build.0 = Debug|Any CPU 20 | {AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}.Release|x86.ActiveCfg = Release|Any CPU 23 | {AC1531B1-1F82-4F36-9E0E-F00D059EEDC4}.Release|x86.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {128C361F-5AA7-401E-832D-CE9BD0BCFBC3} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /AsmTool/IAsmIO.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | namespace AsmTool 11 | { 12 | public interface IAsmIO 13 | { 14 | UInt32 LoadAsmIODriver(); 15 | UInt32 UnloadAsmIODriver(); 16 | UInt32 ReadMEM(UInt32 address, UInt32 size, IntPtr bufPtr); 17 | byte PCI_Read_BYTE(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber, UInt32 offset); 18 | UInt32 PCI_Write_BYTE(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber, UInt32 offset, byte value); 19 | UInt32 PCI_Read_DWORD(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber, UInt32 offset); 20 | 21 | UInt32 ReadCMD(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber, IntPtr bufPtr); 22 | 23 | UInt32 WriteCmdALL( 24 | UInt32 busNumber, 25 | UInt32 deviceNumber, 26 | UInt32 functionNumber, 27 | UInt32 cmd_reg_byte0, 28 | UInt32 cmd_reg_byte1, 29 | UInt32 cmd_reg_byte2, 30 | UInt32 cmd_dat0, 31 | UInt32 cmd_dat1, 32 | UInt32 cmd_dat2); 33 | 34 | UInt32 Wait_Write_Ready(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber); 35 | UInt32 Wait_Read_Ready(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber); 36 | UInt32 MapAsmIO(UInt32 address, UInt32 size); 37 | UInt32 UnmapAsmIO(UInt32 address, UInt32 size); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /AsmTool/Prober.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Diagnostics; 12 | using System.Linq; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | namespace AsmTool 17 | { 18 | public class Prober 19 | { 20 | const uint PCI_BUS_MAX = 256; // 2 ** 8 21 | const uint PCI_DEV_MAX = 32; //2 ** 5 22 | const uint PCI_FUNC_MAX = 8; //2 ** 3 23 | 24 | private readonly IAsmIO io; 25 | 26 | public Prober(IAsmIO io) { 27 | this.io = io; 28 | } 29 | 30 | 31 | public bool FindByProduct(UInt32 pid, out PCIAddress addr) { 32 | for(uint i=0; i {ident:X8}"); 37 | if (ident == 0xFFFFFFFF) { 38 | continue; 39 | } 40 | uint dev_pid = (ident >> 16) & 0xFFFF; 41 | uint dev_vid = (ident & 0xFFFF); 42 | Console.WriteLine($"[bus:{i:X}, dev:{j:X}, func:{k:X}] {dev_vid:X4}:{dev_pid:X4}"); 43 | 44 | if(dev_pid == pid) { 45 | addr = new PCIAddress() { 46 | Bus = i, 47 | Device = j, 48 | Function = k 49 | }; 50 | return true; 51 | } 52 | } 53 | } 54 | } 55 | addr = new PCIAddress() { 56 | Bus = uint.MaxValue, 57 | Device = uint.MaxValue, 58 | Function = uint.MaxValue 59 | }; 60 | return false; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASMTool 2 | Firmware dumper and various utilities for PCI based ASMedia USB Controllers 3 | 4 | It looks like all ICs in the ASM1x4x, ASM2x4x and ASM3x4x family use the same interface and share the same registers, but i only tested this with the ASM2142 Controller that i have in my system. 5 | 6 | # Why? 7 | I'm having issues with my ASM2142 controller (lockup with USB 3.1 and large transfers), and i couldn't find a way to dump the current firmware. 8 | 9 | The firmware updater can internally read the firmware, but it doesn't offer a way to save it. 10 | 11 | # How to use 12 | ## Linux 13 | ```gcc -shared -o libAsmIoLinux.so -fPIC Linux/AsmIOLinux.c``` 14 | Place the resulting `.so` file next to the ASMTool executable (obtained by building this project) 15 | 16 | ## Windows 17 | You'll need `AsmIo.sys` (for 32bit Windows) or `AsmIo64.sys` (for 64bit Windows). 18 | 19 | You will also need `asmiodll.dll`. You can find these files if you google `ASM2142 firmware`. 20 | Download the firmware updater and you'll find the files in there. 21 | 22 | Place all files next to the ASMTool executable (obtained by building this project) 23 | 24 | # How to contribute? 25 | You can either extend this program and add new functionality, or 26 | 27 | open a new Issue and attach the firmware obtained by running this program, so that other users can update their firmwares or try older versions to see if they work better 28 | 29 | # They are custom Intel 8051 cores! 30 | It turns out ASMedia USB controllers are custom Intel 8051 cores, and the firmware file can be disassembled into i8051 assembly 31 | 32 | # Security implications 33 | It looks like this interface could be used to flash malicious code onto ASMedia chips, as explained by 34 | https://chefkochblog.wordpress.com/2018/03/19/asmedia-usb-3-x-controller-with-keylogger-and-malware-risks/ 35 | 36 | The chip performs no signature checks on the code being flashed and, being a PCIe device, could abuse DMA to read and write arbitrary memory 37 | -------------------------------------------------------------------------------- /AsmTool/Program.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Diagnostics; 12 | using System.Linq; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | namespace AsmTool 17 | { 18 | class Program 19 | { 20 | static void Main(string[] args) { 21 | IAsmIO io = AsmIOFactory.GetAsmIO(); 22 | 23 | Console.WriteLine("Unloading ASM Driver..."); 24 | io.UnloadAsmIODriver(); 25 | Console.WriteLine("Loading ASM Driver..."); 26 | if(io.LoadAsmIODriver() != 1) { 27 | Console.Error.WriteLine("Failed to load ASM IO Driver"); 28 | return; 29 | } 30 | 31 | AsmDevice dev = new AsmDevice(io); 32 | 33 | if (args.Length > 0) { 34 | switch (args[0]) { 35 | case "fw_set_type": 36 | { 37 | var patchedFile = Path.Combine( 38 | Path.GetDirectoryName(args[1]), 39 | Path.GetFileNameWithoutExtension(args[1]) + "_patched.bin" 40 | ); 41 | File.Copy(args[1], patchedFile, true); 42 | using var fw = new AsmFirmware(patchedFile); 43 | var newType = args[2] == "2142" 44 | ? AsmFirmwareChipType.Asm2142 45 | : AsmFirmwareChipType.Asm3142; 46 | Console.WriteLine($"Setting {newType}"); 47 | fw.SetChipType(newType); 48 | break; 49 | } 50 | case "fw_info": { 51 | using var fw = new AsmFirmware(args[1]); 52 | fw.PrintInfo(dev, Console.Out); 53 | break; 54 | } 55 | case "mem_read": 56 | dev.DumpMemory(); 57 | break; 58 | case "flash_read": 59 | default: 60 | Console.WriteLine("Dumping firmware..."); 61 | dev.DumpFirmware("dump.bin"); 62 | break; 63 | } 64 | } 65 | 66 | io.UnloadAsmIODriver(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /AsmTool/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 59 | 60 | 61 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /AsmTool/LinuxAsmIO.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | using System.Runtime.InteropServices; 11 | using System.Threading; 12 | 13 | namespace AsmTool 14 | { 15 | public class LinuxNativeIO 16 | { 17 | [DllImport("libAsmIoLinux")] 18 | public static extern uint LoadAsmIODriver(); 19 | 20 | [DllImport("libAsmIoLinux")] 21 | public static extern byte PCI_Read_BYTE(uint busNumber, uint deviceNumber, uint functionNumber, uint offset); 22 | [DllImport("libAsmIoLinux")] 23 | public static extern uint PCI_Write_BYTE(uint busNumber, uint deviceNumber, uint functionNumber, uint offset, byte value); 24 | 25 | [DllImport("libAsmIoLinux")] 26 | public static extern uint PCI_Read_DWORD(uint busNumber, uint deviceNumber, uint functionNumber, uint offset); 27 | 28 | [DllImport("libAsmIoLinux")] 29 | public static extern uint Wait_Read_Ready(uint busNumber, uint deviceNumber, uint functionNumber); 30 | 31 | [DllImport("libAsmIoLinux")] 32 | public static extern uint Wait_Write_Ready(uint busNumber, uint deviceNumber, uint functionNumber); 33 | 34 | [DllImport("libAsmIoLinux")] 35 | public static extern uint ReadCMD(uint busNumber, uint deviceNumber, uint functionNumber, IntPtr pBuf); 36 | 37 | [DllImport("libAsmIoLinux")] 38 | public static extern uint WriteCmdALL(uint busNumber, uint deviceNumber, uint functionNumber, uint cmd_reg_byte0, uint cmd_reg_byte1, uint cmd_reg_byte2, uint cmd_dat0, uint cmd_dat1, uint cmd_dat2); 39 | } 40 | 41 | 42 | 43 | public class LinuxAsmIO : IAsmIO 44 | { 45 | public uint LoadAsmIODriver() { 46 | return LinuxNativeIO.LoadAsmIODriver(); 47 | } 48 | 49 | public uint MapAsmIO(uint address, uint size) { 50 | throw new NotImplementedException(); 51 | } 52 | 53 | public byte PCI_Read_BYTE(uint busNumber, uint deviceNumber, uint functionNumber, uint offset) { 54 | return LinuxNativeIO.PCI_Read_BYTE(busNumber, deviceNumber, functionNumber, offset); 55 | } 56 | 57 | public uint PCI_Read_DWORD(uint busNumber, uint deviceNumber, uint functionNumber, uint offset) { 58 | return LinuxNativeIO.PCI_Read_DWORD(busNumber, deviceNumber, functionNumber, offset); 59 | } 60 | 61 | public uint PCI_Write_BYTE(uint busNumber, uint deviceNumber, uint functionNumber, uint offset, byte value) { 62 | return LinuxNativeIO.PCI_Write_BYTE(busNumber, deviceNumber, functionNumber, offset, value); 63 | } 64 | 65 | public uint ReadCMD(uint busNumber, uint deviceNumber, uint functionNumber, IntPtr bufPtr) { 66 | return LinuxNativeIO.ReadCMD(busNumber, deviceNumber, functionNumber, bufPtr); 67 | } 68 | 69 | public uint ReadMEM(uint address, uint size, IntPtr bufPtr) { 70 | throw new NotImplementedException(); 71 | } 72 | 73 | public uint UnloadAsmIODriver() { 74 | return 1; 75 | } 76 | 77 | public uint UnmapAsmIO(uint address, uint size) { 78 | // $TODO 79 | throw new NotImplementedException(); 80 | } 81 | 82 | public uint Wait_Read_Ready(uint busNumber, uint deviceNumber, uint functionNumber) { 83 | return LinuxNativeIO.Wait_Read_Ready(busNumber, deviceNumber, functionNumber); 84 | } 85 | 86 | public uint Wait_Write_Ready(uint busNumber, uint deviceNumber, uint functionNumber) { 87 | return LinuxNativeIO.Wait_Write_Ready(busNumber, deviceNumber, functionNumber); 88 | } 89 | 90 | public uint WriteCmdALL(uint busNumber, uint deviceNumber, uint functionNumber, uint cmd_reg_byte0, uint cmd_reg_byte1, uint cmd_reg_byte2, uint cmd_dat0, uint cmd_dat1, uint cmd_dat2) { 91 | return LinuxNativeIO.WriteCmdALL(busNumber, deviceNumber, functionNumber, cmd_reg_byte0, cmd_reg_byte1, cmd_reg_byte2, cmd_dat0, cmd_dat1, cmd_dat2); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /AsmTool/Linux/AsmIOLinux.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2019 Stefano Moioli 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef unsigned int uint; 13 | typedef unsigned char byte; 14 | 15 | #define ASM_REG_REGSEL 0xF8 16 | #define ASM_REG_REGDAT_IN 0xFC 17 | #define ASM_REG_REGDAT_OUT1 0xF0 18 | #define ASM_REG_REGDAT_OUT2 0xF4 19 | #define ASM_REG_CTRL 0xE0 20 | 21 | #define DPRINTF(fmt, ...) \ 22 | fprintf(stderr, "[%s:%d] " fmt, __func__, __LINE__, ##__VA_ARGS__) 23 | 24 | struct PciAddr { 25 | uint32_t bus; 26 | uint32_t dev; 27 | uint32_t func; 28 | }; 29 | 30 | struct AsmPacket { 31 | struct PciAddr addr; 32 | uint32_t data1; 33 | uint32_t data2; 34 | uint8_t unk[24]; 35 | }; 36 | 37 | // read is not exclusive 38 | #define READ_BIT_FLAG 1 39 | // write is exclusive 40 | #define WRITE_BIT_FLAG 2 41 | 42 | static struct pci_access *pacc = NULL; 43 | 44 | void __attribute__((destructor)) dtor(){ 45 | if(!pacc) return; 46 | pci_cleanup(pacc); 47 | pacc = NULL; 48 | } 49 | 50 | uint LoadAsmIODriver(){ 51 | if(!pacc){ 52 | pacc = pci_alloc(); 53 | if(!pacc){ 54 | return 0; 55 | } 56 | pci_init(pacc); 57 | } 58 | return 1; 59 | } 60 | 61 | uint32_t PCI_Read_DWORD(uint bus, uint dev, uint func, uint offset){ 62 | struct pci_dev *pdev = pci_get_dev(pacc, 0, bus, dev, func); 63 | if(!pdev){ 64 | return -1; 65 | } 66 | return pci_read_long(pdev, offset); 67 | } 68 | 69 | byte PCI_Read_BYTE(uint bus, uint dev, uint func, uint offset){ 70 | struct pci_dev *pdev = pci_get_dev(pacc, 0, bus, dev, func); 71 | if(!pdev){ 72 | return -1; 73 | } 74 | return pci_read_byte(pdev, offset); 75 | } 76 | 77 | uint PCI_Write_BYTE(uint bus, uint dev, uint func, uint offset, byte value){ 78 | struct pci_dev *pdev = pci_get_dev(pacc, 0, bus, dev, func); 79 | if(!pdev){ 80 | return -1; 81 | } 82 | pci_write_byte(pdev, offset, value); 83 | return 0; 84 | } 85 | 86 | uint Wait_Read_Ready(uint bus, uint dev, uint func){ 87 | byte flag; 88 | int cnt = 0; 89 | 90 | struct pci_dev *pdev = pci_get_dev(pacc, 0, bus, dev, func); 91 | if(!pdev){ 92 | return -1; 93 | } 94 | 95 | for(;cnt < 20000; cnt++){ 96 | for( 97 | flag = pci_read_byte(pdev, ASM_REG_CTRL);; 98 | cnt++, flag = pci_read_byte(pdev, ASM_REG_CTRL) 99 | ){ 100 | if(flag == 0xFF){ 101 | if(++cnt >= 10) 102 | return -2; 103 | } 104 | 105 | if(!(flag & READ_BIT_FLAG)) 106 | break; 107 | 108 | flag = pci_read_byte(pdev, ASM_REG_CTRL); 109 | if(flag & READ_BIT_FLAG){ 110 | return 0; 111 | } 112 | } 113 | } 114 | 115 | return -1; 116 | } 117 | 118 | uint Wait_Write_Ready(uint bus, uint dev, uint func){ 119 | byte flag; 120 | int cnt = 0; 121 | 122 | struct pci_dev *pdev = pci_get_dev(pacc, 0, bus, dev, func); 123 | if(!pdev){ 124 | return -1; 125 | } 126 | 127 | for(;cnt < 20000; cnt++){ 128 | for( 129 | flag = pci_read_byte(pdev, ASM_REG_CTRL);; 130 | cnt++, flag = pci_read_byte(pdev, ASM_REG_CTRL) 131 | ){ 132 | if(flag == 0xFF){ 133 | if(++cnt >= 10) 134 | return -2; 135 | } 136 | 137 | if(flag & WRITE_BIT_FLAG) 138 | break; 139 | 140 | flag = pci_read_byte(pdev, ASM_REG_CTRL); 141 | if(!(flag & WRITE_BIT_FLAG)){ 142 | return 0; 143 | } 144 | } 145 | } 146 | 147 | return -1; 148 | } 149 | 150 | static uint32_t ComputeInternalRegister(byte b0, byte b1, byte b2){ 151 | return (0 152 | | (b2 << 16) 153 | | (b1 << 8) 154 | | b0 155 | ); 156 | } 157 | 158 | uint ReadCMD( 159 | uint bus, uint dev, uint func, struct AsmPacket *outPacket 160 | ){ 161 | struct pci_dev *pdev = pci_get_dev(pacc, 0, bus, dev, func); 162 | if(!pdev){ 163 | return -1; 164 | } 165 | 166 | outPacket->data1 = pci_read_long(pdev, ASM_REG_REGDAT_OUT1); 167 | outPacket->data2 = pci_read_long(pdev, ASM_REG_REGDAT_OUT2); 168 | pci_write_byte(pdev, ASM_REG_CTRL, READ_BIT_FLAG); 169 | return 0; 170 | } 171 | 172 | static inline void __attribute__((always_inline)) AsmCmd( 173 | struct pci_dev *pdev, 174 | uint reg, uint data, uint type 175 | ){ 176 | pci_write_long(pdev, ASM_REG_REGSEL, reg); 177 | pci_write_long(pdev, ASM_REG_REGDAT_IN, data); 178 | pci_write_byte(pdev, ASM_REG_CTRL, type); 179 | } 180 | 181 | uint WriteCmdALL( 182 | uint bus, uint dev, uint func, 183 | byte cmd_reg_b0, byte cmd_reg_b1, byte cmd_reg_b2, 184 | uint cmd_dat0, uint cmd_dat1, uint cmd_dat2 185 | ){ 186 | struct pci_dev *pdev = pci_get_dev(pacc, 0, bus, dev, func); 187 | if(!pdev){ 188 | return -1; 189 | } 190 | 191 | uint32_t reg = ComputeInternalRegister(cmd_reg_b0, cmd_reg_b1, cmd_reg_b2); 192 | 193 | int rc; 194 | if((rc = Wait_Write_Ready(bus, dev, func)) < 0){ 195 | DPRINTF("Wait_Write_Ready failed! (rc=%d)\n", rc); 196 | return rc; 197 | } 198 | 199 | AsmCmd(pdev, reg, cmd_dat0, WRITE_BIT_FLAG); 200 | 201 | if((rc = Wait_Write_Ready(bus, dev, func)) < 0){ 202 | DPRINTF("Wait_Write_Ready failed (awaiting first command)! (rc=%d)\n", rc); 203 | return rc; 204 | } 205 | 206 | AsmCmd(pdev, cmd_dat1, cmd_dat2, WRITE_BIT_FLAG); 207 | return 0; 208 | } 209 | -------------------------------------------------------------------------------- /AsmTool/WindowsAsmIO.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Runtime.InteropServices; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | namespace AsmTool 17 | { 18 | public struct AsmIOPacket 19 | { 20 | /* 0 */ public PCIAddress Dev; 21 | /* 12 */ public UInt32 Data1; 22 | /* 16 */ public UInt32 Data2; 23 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] 24 | /* 20 */ public byte[] Unknown; 25 | } 26 | 27 | public class AsmIODll 28 | { 29 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_LoadAsmIODriver@0", ExactSpelling = true)] 30 | public static extern UInt32 LoadAsmIODriver(); 31 | 32 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_UnloadAsmIODriver@0", ExactSpelling = true)] 33 | public static extern UInt32 UnloadAsmIODriver(); 34 | 35 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_ReadMEM@12", ExactSpelling = true)] 36 | public static extern UInt32 ReadMEM(UInt32 address, UInt32 size, IntPtr bufPtr); 37 | 38 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_PCI_Read_BYTE@16", ExactSpelling = true)] 39 | public static extern byte PCI_Read_BYTE(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber, UInt32 offset); 40 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_PCI_Write_Byte@20", ExactSpelling = true)] 41 | public static extern byte PCI_Write_BYTE(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber, UInt32 offset, UInt32 value); 42 | 43 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_PCI_Read_DWORD@16", ExactSpelling = true)] 44 | public static extern UInt32 PCI_Read_DWORD(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber, UInt32 offset); 45 | 46 | 47 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_ReadCMD@16", ExactSpelling = true)] 48 | public static extern UInt32 ReadCMD(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber, IntPtr bufPtr); 49 | 50 | /// 51 | /// 52 | /// 53 | /// PCI Bus number 54 | /// PCI Device number 55 | /// PCI Function number 56 | /// Byte0 of internal register (low byte - selects function?) 57 | /// Byte1 of internal register (middle byte - selects device?) 58 | /// Byte2 of internal register (high byte - selects function?) 59 | /// Data to write to the internal register 60 | /// Extra data 61 | /// Extra data 62 | /// 63 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_WriteCmdALL@36", ExactSpelling = true)] 64 | public static extern UInt32 WriteCmdALL( 65 | /* a0 */ UInt32 busNumber, //ioctl arg 0 66 | /* a1 */ UInt32 deviceNumber, //ioctl arg 1 67 | /* a2 */ UInt32 functionNumber, //ioctl arg 2 68 | 69 | // ioctl arg3, arg4 are reserved for computation of the internal register 70 | 71 | /* a3 */ UInt32 cmd_reg_byte0, //ioctl arg 5 72 | /* a4 */ UInt32 cmd_reg_byte1, //ioctl arg 6 73 | /* a5 */ UInt32 cmd_reg_byte2, //ioctl arg 7, also word size 74 | /* a6 */ UInt32 cmd_dat0, //ioctl arg 8 75 | /* a7 */ UInt32 cmd_dat1, //ioctl arg 9 76 | /* a8 */ UInt32 cmd_dat2 //ioctl arg 10 77 | ); 78 | 79 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_Wait_Write_Ready@12", ExactSpelling = true)] 80 | public static extern UInt32 Wait_Write_Ready(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber); 81 | 82 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_Wait_Read_Ready@12", ExactSpelling = true)] 83 | public static extern UInt32 Wait_Read_Ready(UInt32 busNumber, UInt32 deviceNumber, UInt32 functionNumber); 84 | 85 | 86 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_MapAsmIO@8", ExactSpelling = true)] 87 | public static extern UInt32 MapAsmIO(UInt32 address, UInt32 size); 88 | 89 | [DllImport("asmiodll", CallingConvention = CallingConvention.StdCall, EntryPoint = "_UnmapAsmIO@8", ExactSpelling = true)] 90 | public static extern UInt32 UnmapAsmIO(UInt32 address, UInt32 size); 91 | } 92 | 93 | public class WindowsAsmIO : IAsmIO 94 | { 95 | public uint LoadAsmIODriver() => AsmIODll.LoadAsmIODriver(); 96 | public uint MapAsmIO(uint address, uint size) => AsmIODll.MapAsmIO(address, size); 97 | public byte PCI_Read_BYTE(uint busNumber, uint deviceNumber, uint functionNumber, uint offset) => AsmIODll.PCI_Read_BYTE(busNumber, deviceNumber, functionNumber, offset); 98 | public uint PCI_Read_DWORD(uint busNumber, uint deviceNumber, uint functionNumber, uint offset) => AsmIODll.PCI_Read_DWORD(busNumber, deviceNumber, functionNumber, offset); 99 | public uint PCI_Write_BYTE(uint busNumber, uint deviceNumber, uint functionNumber, uint offset, byte value) => AsmIODll.PCI_Write_BYTE(busNumber, deviceNumber, functionNumber, offset, value); 100 | 101 | public uint ReadCMD(uint busNumber, uint deviceNumber, uint functionNumber, IntPtr bufPtr) => AsmIODll.ReadCMD(busNumber, deviceNumber, functionNumber, bufPtr); 102 | public uint ReadMEM(uint address, uint size, IntPtr bufPtr) => AsmIODll.ReadMEM(address, size, bufPtr); 103 | public uint UnloadAsmIODriver() => AsmIODll.UnloadAsmIODriver(); 104 | public uint UnmapAsmIO(uint address, uint size) => AsmIODll.UnmapAsmIO(address, size); 105 | public uint Wait_Read_Ready(uint busNumber, uint deviceNumber, uint functionNumber) => AsmIODll.Wait_Read_Ready(busNumber, deviceNumber, functionNumber); 106 | public uint Wait_Write_Ready(uint busNumber, uint deviceNumber, uint functionNumber) => AsmIODll.Wait_Write_Ready(busNumber, deviceNumber, functionNumber); 107 | public uint WriteCmdALL(uint busNumber, uint deviceNumber, uint functionNumber, uint cmd_reg_byte0, uint cmd_reg_byte1, uint cmd_reg_byte2, uint cmd_dat0, uint cmd_dat1, uint cmd_dat2) { 108 | return AsmIODll.WriteCmdALL( 109 | busNumber, deviceNumber, functionNumber, 110 | cmd_reg_byte0, cmd_reg_byte1, cmd_reg_byte2, 111 | cmd_dat0, cmd_dat1, cmd_dat2 112 | ); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /AsmTool/AsmFirmware.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2023 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using Smx.SharpIO; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | namespace AsmTool 17 | { 18 | public enum AsmFirmwareChipType : byte { 19 | Asm2142 = 0x50, 20 | Asm3142 = 0x70 21 | } 22 | 23 | public class AsmFirmware : IDisposable 24 | { 25 | private readonly string filePath; 26 | private readonly MFile mf; 27 | private readonly SpanStream stream; 28 | 29 | private const string MAGIC_GEN1 = "2114A_RCFG"; 30 | private const string MAGIC_GEN2 = "2214A_RCFG"; 31 | 32 | private Span Span { 33 | get { 34 | return mf.Span.Memory.Span; 35 | } 36 | } 37 | 38 | public AsmFirmware(string firmwarePath) { 39 | filePath = firmwarePath; 40 | mf = MFile.Open(firmwarePath, FileMode.Open, 41 | FileAccess.Read | FileAccess.Write, FileShare.Read); 42 | stream = new SpanStream(mf); 43 | } 44 | 45 | private void UpdateChecksum() { 46 | Span[HeaderChecksumOffset] = ComputeHeaderChecksum(); 47 | Span[BodyChecksumOffset] = ComputeBodyChecksum(); 48 | } 49 | 50 | public void SetChipType(AsmFirmwareChipType type) { 51 | Span[0xBC] = (byte)type; 52 | UpdateChecksum(); 53 | } 54 | 55 | private int GetSignatureType() { 56 | string magic = ReadStringSignature(); 57 | switch(magic) { 58 | case MAGIC_GEN1: return 0; 59 | case MAGIC_GEN2: return 1; 60 | default: 61 | throw new InvalidDataException($"Unexpected magic \"{magic}\""); 62 | } 63 | } 64 | 65 | private byte[] GetFirmwareVersion() { 66 | var fwVer = stream.PerformAt(0xB9, () => { 67 | return stream.ReadBytes(6); 68 | }); 69 | return fwVer; 70 | } 71 | 72 | private AsmFirmwareChipType GetFirmwareChipType() { 73 | var fwVer = GetFirmwareVersion(); 74 | switch (fwVer[3]) { 75 | case 0x50: return AsmFirmwareChipType.Asm2142; 76 | case 0x70: return AsmFirmwareChipType.Asm3142; 77 | default: throw new InvalidDataException($"Unknown chip id {fwVer[3]:X2}"); 78 | } 79 | } 80 | 81 | private string GetFirmwareVersionString() { 82 | var fwVer = GetFirmwareVersion(); 83 | return string.Format("{0:X2}{1:X2}{2:X2}_{3:X2}_{4:X2}_{5:X2}", 84 | // firmware version 85 | fwVer[0], fwVer[1], fwVer[2], 86 | // chip type (0x50: 2142, 0x70: 3142) 87 | fwVer[3], 88 | // unk (chip sub-type?) 89 | fwVer[4], 90 | fwVer[5] 91 | ); 92 | } 93 | 94 | private string GetFirmwareName() { 95 | var fwChipName = stream.PerformAt(0xB9 + 7, () => { 96 | return stream.ReadCString(Encoding.ASCII); 97 | }); 98 | return fwChipName; 99 | } 100 | 101 | private string ReadFooterSignature() { 102 | var offset = HeaderSize + BodySize + 9; 103 | return stream.PerformAt(offset, () => { 104 | return stream.ReadString(8, Encoding.ASCII); 105 | }); 106 | } 107 | 108 | private string ReadStringSignature() { 109 | return stream.PerformAt(6, () => { 110 | return stream.ReadString(10, Encoding.ASCII); 111 | }); 112 | } 113 | 114 | private ushort HeaderSize { 115 | get { 116 | return (ushort)((ushort)0u 117 | | (ushort)(Span[4] << 0) 118 | | (ushort)(Span[5] << 8) 119 | ); 120 | } 121 | } 122 | 123 | private uint BodySize { 124 | get { 125 | return (0u 126 | | (uint)(Span[HeaderSize + 5] << 0) 127 | | (uint)(Span[HeaderSize + 6] << 8) 128 | | (uint)(Span[HeaderSize + 7] << 16) 129 | ); 130 | } 131 | } 132 | 133 | private int HeaderChecksumOffset => HeaderSize; 134 | private int BodyChecksumOffset => (int)(HeaderSize + BodyStartOffset + BodySize + 8); 135 | 136 | 137 | private byte HeaderChecksum => Span[HeaderChecksumOffset]; 138 | private byte BodyChecksum => Span[BodyChecksumOffset]; 139 | 140 | private int BodyStartOffset { 141 | get { 142 | if (ReadStringSignature() == MAGIC_GEN1) return 7; 143 | return 9; 144 | } 145 | } 146 | 147 | private byte ComputeBodyChecksum() { 148 | var body_start_offset = BodyStartOffset; 149 | 150 | byte p0 = 0; 151 | byte p1 = 0; 152 | int i = 0; 153 | 154 | var body_start = HeaderSize + body_start_offset; 155 | if (BodySize >= 2) { 156 | for (i = 0; i < BodySize; i += 2) { 157 | p0 += Span[body_start + i]; 158 | p1 += Span[body_start + i + 1]; 159 | } 160 | } 161 | 162 | byte p2; 163 | if (i >= BodySize) { 164 | p2 = 0; 165 | } else { 166 | p2 = Span[body_start + i]; 167 | } 168 | 169 | byte checksum = 0; 170 | checksum += p0; 171 | checksum += p1; 172 | checksum += p2; 173 | return checksum; 174 | } 175 | 176 | private byte ComputeHeaderChecksum() { 177 | byte p0 = 0; 178 | byte p1 = 0; 179 | int i = 0; 180 | if (HeaderSize >= 2) { 181 | for (i = 0; i < HeaderSize; i += 2) { 182 | p0 += Span[i]; 183 | p1 += Span[i + 1]; 184 | } 185 | } 186 | 187 | byte p2; 188 | if (i >= HeaderSize) { 189 | p2 = 0; 190 | } else { 191 | p2 = Span[i]; 192 | } 193 | 194 | byte checksum = 0; 195 | checksum += p0; 196 | checksum += p1; 197 | checksum += p2; 198 | return checksum; 199 | 200 | } 201 | 202 | public void PrintInfo(AsmDevice dev, TextWriter os) { 203 | os.WriteLine("==== File Info ===="); 204 | os.WriteLine($"File: {filePath}"); 205 | 206 | var compHeaderChecksum = ComputeHeaderChecksum(); 207 | var compBodyChecksum = ComputeBodyChecksum(); 208 | 209 | os.WriteLine($"File Checksum [header]: 0x{HeaderChecksum:X2} (expected: 0x{compHeaderChecksum:X2})"); 210 | os.WriteLine($"File Checksum [body]: 0x{BodyChecksum:X2} (expected: 0x{compBodyChecksum:X2})"); 211 | 212 | if (HeaderChecksum != compHeaderChecksum || BodyChecksum != compBodyChecksum) { 213 | os.WriteLine("!! WARNING: Checksum Mismatch"); 214 | } else { 215 | os.WriteLine("Checksum OK"); 216 | } 217 | 218 | os.WriteLine($"Signature: " + ReadStringSignature()); 219 | os.WriteLine($"FW Name: " + GetFirmwareName()); 220 | var fwChipType = GetFirmwareChipType(); 221 | var fwChipName = fwChipType switch { 222 | AsmFirmwareChipType.Asm2142 => "ASM2142", 223 | AsmFirmwareChipType.Asm3142 => "ASM3142", 224 | _ => "Unknown" 225 | }; 226 | os.WriteLine($"Footer: " + ReadFooterSignature()); 227 | 228 | os.WriteLine($"Chip: 0x{(byte)fwChipType:X2}: {fwChipName}"); 229 | 230 | os.WriteLine("==== Actual Chip Info ===="); 231 | 232 | var chipRev0 = dev.ReadMemory(0x150B2)?[0]; 233 | var chipRev1 = dev.ReadMemory(0xF38C)?[0]; 234 | 235 | if(chipRev0 != null) { 236 | os.WriteLine($"Chip Rev0: 0x{chipRev0:X2}"); 237 | } 238 | if(chipRev1 != null) { 239 | os.WriteLine($"Chip Rev1: 0x{chipRev1:X2}"); 240 | } 241 | 242 | } 243 | 244 | public void Dispose() { 245 | mf.Dispose(); 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /AsmTool/AsmDevice.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Copyright (C) 2019 Stefano Moioli 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | */ 8 | #endregion 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Diagnostics; 12 | using System.IO; 13 | using System.Linq; 14 | using System.Runtime.InteropServices; 15 | using System.Text; 16 | using System.Threading; 17 | using System.Threading.Tasks; 18 | 19 | namespace AsmTool 20 | { 21 | public class AsmDevice { 22 | const uint PID_2142 = 0x2142; 23 | const uint PID_3142 = 0x3142; 24 | 25 | const uint FIRMWARE_SIZE = 131072; //128k ROM 26 | 27 | private readonly Prober prb; 28 | private readonly PCIAddress pcidev; 29 | 30 | /// 31 | /// base address register 32 | /// 33 | private readonly PCIBar bar; 34 | 35 | private readonly IAsmIO io; 36 | 37 | public AsmDevice(IAsmIO io) { 38 | this.io = io; 39 | this.prb = new Prober(io); 40 | 41 | Console.WriteLine("Scanning for ASMedia ICs..."); 42 | if (!prb.FindByProduct(PID_2142, out pcidev) && !prb.FindByProduct(PID_3142, out pcidev)) 43 | throw new Exception($"No ASMedia device detected!"); 44 | 45 | Console.WriteLine("Found ASMedia IC!"); 46 | uint barValue = PCIReadWord(0x10); 47 | Console.WriteLine($"BAR: {barValue:X8}"); 48 | 49 | this.bar = new PCIBar(barValue); 50 | } 51 | 52 | public AsmMemory NewMemoryMap(uint offset, uint size) { 53 | return new AsmMemory(io, this.bar.BaseAddress + offset, size); 54 | } 55 | 56 | public uint PCIReadWord(uint offset) { 57 | return io.PCI_Read_DWORD(pcidev.Bus, pcidev.Device, pcidev.Function, offset); 58 | } 59 | 60 | public byte PCIReadByte(uint offset) { 61 | return io.PCI_Read_BYTE(pcidev.Bus, pcidev.Device, pcidev.Function, offset); 62 | } 63 | 64 | private static byte[] BuildAsmCommand( 65 | ASMIOCommand mode, ASMIOFunction function, byte size 66 | ) { 67 | return new byte[] { 68 | (byte)mode, (byte)function, size 69 | }; 70 | } 71 | 72 | private UInt32 WriteRegister(byte[] reg, uint data, uint data2 = 0, uint data3 = 0) { 73 | return io.WriteCmdALL(pcidev.Bus, pcidev.Device, pcidev.Function, 74 | reg[0], reg[1], reg[2], 75 | data, data2, data3 76 | ); 77 | } 78 | 79 | private bool WaitWrite() { 80 | if(io.Wait_Write_Ready(pcidev.Bus, pcidev.Device, pcidev.Function) < 0) { 81 | return false; 82 | } 83 | return true; 84 | } 85 | 86 | public unsafe bool WriteMemory(uint address, byte[] bytes) { 87 | var reg = BuildAsmCommand(ASMIOCommand.Write, ASMIOFunction.Memory, (byte)bytes.Length); 88 | 89 | 90 | var span = bytes.AsSpan(); 91 | while(span.Length > 0) { 92 | var length = Math.Min(8, span.Length); 93 | 94 | ulong value = 0; 95 | for (int i = 0; i < length; i++) { 96 | value |= (uint)(span[i] << (i * 8)); 97 | } 98 | 99 | span = span.Slice(length); 100 | 101 | uint word0 = (uint)(value >> 0); 102 | uint word1 = (uint)(value >> 32); 103 | Trace.WriteLine($"W0 is {word0}, W1 is {word1}"); 104 | 105 | if (WriteRegister(reg, address, word0, word1) < 0) { 106 | return false; 107 | } 108 | if (!WaitWrite()) { 109 | return false; 110 | } 111 | } 112 | 113 | 114 | return true; 115 | } 116 | 117 | public unsafe byte[]? ReadMemory(uint address) { 118 | byte wordSize = 4; 119 | var reg = BuildAsmCommand(ASMIOCommand.Read, ASMIOFunction.Memory, wordSize); 120 | if (WriteRegister(reg, address) < 0) { 121 | Console.WriteLine("WriteRegister failed!"); 122 | return null; 123 | } 124 | 125 | if (!ReadPacket(wordSize, out byte[]? ack) || ack == null) { 126 | Console.WriteLine("Failed to read ack!"); 127 | return null; 128 | } 129 | 130 | byte[] word = new byte[wordSize]; 131 | if (!ReadPacketSmall(wordSize, word)) { 132 | Console.WriteLine("Failed to read data!"); 133 | return null; 134 | } 135 | return word; 136 | } 137 | 138 | public unsafe bool ReadPacketSmall(int wordSize, byte[] data) { 139 | if (io.Wait_Read_Ready(pcidev.Bus, pcidev.Device, pcidev.Function) < 0) { 140 | Console.WriteLine("Wait_Read_Ready failed!"); 141 | return false; 142 | } 143 | 144 | for (int i = 0; i < wordSize; i++) { 145 | byte offset = (byte)(0xF0 + i); 146 | data[i] = io.PCI_Read_BYTE(pcidev.Bus, pcidev.Device, pcidev.Function, offset); 147 | } 148 | // signal read end 149 | io.PCI_Write_BYTE(pcidev.Bus, pcidev.Device, pcidev.Function, 0xE0, 1); 150 | 151 | return true; 152 | } 153 | 154 | public unsafe bool ReadPacket(uint wordSize, out byte[]? data) { 155 | data = null; 156 | 157 | if (io.Wait_Read_Ready(pcidev.Bus, pcidev.Device, pcidev.Function) < 0) { 158 | Console.WriteLine("Wait_Read_Ready failed!"); 159 | return false; 160 | } 161 | 162 | data = new byte[0x2C]; 163 | fixed (byte* ptr = data) { 164 | io.ReadCMD(pcidev.Bus, pcidev.Device, pcidev.Function, new IntPtr(ptr)); 165 | } 166 | return true; 167 | } 168 | 169 | private static unsafe T? ReadStructure(byte[] data) { 170 | fixed (byte* ptr = data) { 171 | return Marshal.PtrToStructure(new IntPtr(ptr)); 172 | } 173 | } 174 | 175 | public unsafe bool DumpFirmware(string filename) { 176 | uint num_reads = FIRMWARE_SIZE / 8; 177 | 178 | BinaryWriter bw = new BinaryWriter(new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)); 179 | for (uint i=0; i(reply); 215 | 216 | // assemble qword in little endian order due to BinaryWriter being LE only 217 | qword = ((ulong)(pkt.Data2) << 32) | pkt.Data1; 218 | 219 | return true; 220 | } 221 | 222 | public void DumpMemory() { 223 | var MEM_SIZE = 128*1024 ; 224 | using var fh = new FileStream("mem.bin", 225 | FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); 226 | fh.SetLength(0); 227 | 228 | var wordSize = 4; 229 | for (int i = 0; i < MEM_SIZE; i+=wordSize) { 230 | var data = ReadMemory((uint)i); 231 | if (data != null) { 232 | Console.WriteLine("writing"); 233 | fh.Write(data); 234 | } else { 235 | break; 236 | } 237 | } 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /cmake/FindDotnet.cmake: -------------------------------------------------------------------------------- 1 | #.rst 2 | # FindDotnet 3 | # This file is forked from https://github.com/microsoft/GraphEngine/blob/master/cmake/FindDotnet.cmake 4 | # it has been modified by Stefano Moioli for the Reko project 5 | # ---------- 6 | # 7 | # Find DotNet executable, and initialize functions for adding dotnet projects. 8 | # 9 | # Results are reported in the following variables:: 10 | # 11 | # DOTNET_FOUND - True if dotnet executable is found 12 | # DOTNET_EXE - Dotnet executable 13 | # DOTNET_VERSION - Dotnet version as reported by dotnet executable 14 | # NUGET_EXE - Nuget executable (WIN32 only) 15 | # NUGET_CACHE_PATH - Nuget package cache path 16 | # 17 | # The following functions are defined to add dotnet/msbuild projects: 18 | # 19 | # ADD_DOTNET -- add a project to be built by dotnet. 20 | # 21 | # ``` 22 | # ADD_DOTNET( [RELEASE|DEBUG] [X86|X64|ANYCPU] [NETCOREAPP] 23 | # [CONFIG configuration] 24 | # [PLATFORM platform] 25 | # [PACKAGE output_nuget_packages... ] 26 | # [VERSION nuget_package_version] 27 | # [DEPENDS depend_nuget_packages... ] 28 | # [OUTPUT_PATH output_path relative to cmake binary output dir] 29 | # [CUSTOM_BUILDPROPS value....] 30 | # [SOURCES additional_file_dependencies... ] 31 | # [ARGUMENTS additional_build_args...] 32 | # [PACK_ARGUMENTS additional_pack_args...]) 33 | # ``` 34 | # 35 | # RUN_DOTNET -- Run a project with `dotnet run`. The `OUTPUT` argument represents artifacts 36 | # produced by running the .NET program, and can be consumed from other build steps. 37 | # 38 | # ``` 39 | # RUN_DOTNET( [RELEASE|DEBUG] [X86|X64|ANYCPU] [NETCOREAPP] 40 | # [ARGUMENTS program_args...] 41 | # [OUTPUT outputs...] 42 | # [CONFIG configuration] 43 | # [PLATFORM platform] 44 | # [DEPENDS depend_nuget_packages... ] 45 | # [OUTPUT_PATH output_path relative to cmake binary output dir] 46 | # [CUSTOM_BUILDPROPS value....] 47 | # [SOURCES additional_file_dependencies... ]) 48 | # ``` 49 | # 50 | # ADD_MSBUILD -- add a project to be built by msbuild. Windows-only. When building in Unix systems, msbuild targets are skipped. 51 | # 52 | # ``` 53 | # ADD_MSBUILD( [RELEASE|DEBUG] [X86|X64|ANYCPU] [NETCOREAPP] 54 | # [CONFIG configuration] 55 | # [PLATFORM platform] 56 | # [PACKAGE output_nuget_packages... ] 57 | # [DEPENDS depend_nuget_packages... ] 58 | # [CUSTOM_BUILDPROPS value....] 59 | # [SOURCES additional_file_dependencies... ] 60 | # [ARGUMENTS additional_build_args...] 61 | # [PACK_ARGUMENTS additional_pack_args...]) 62 | # ``` 63 | # 64 | # SMOKETEST_DOTNET -- add a dotnet smoke test project to the build. The project will be run during a build, 65 | # and if the program fails to build or run, the build fails. Currently only .NET Core App framework is supported. 66 | # Multiple smoke tests will be run one-by-one to avoid global resource conflicts. 67 | # 68 | # SMOKETEST_DOTNET( [RELEASE|DEBUG] [X86|X64|ANYCPU] [NETCOREAPP] 69 | # [ARGUMENTS program_args...] 70 | # [CONFIG configuration] 71 | # [PLATFORM platform] 72 | # [DEPENDS depend_nuget_packages... ] 73 | # [OUTPUT_PATH output_path relative to cmake binary output dir] 74 | # [CUSTOM_BUILDPROPS value....] 75 | # [SOURCES additional_file_dependencies... ]) 76 | # 77 | # For all the above functions, `RELEASE|DEBUG` overrides `CONFIG`, `X86|X64|ANYCPU` overrides PLATFORM. 78 | # For Unix systems, the target framework defaults to `net8.0`, unless `NETCOREAPP` is specified. 79 | # For Windows, the project is built as-is, allowing multi-targeting. 80 | # 81 | # 82 | # DOTNET_REGISTER_LOCAL_REPOSITORY -- register a local NuGet package repository. 83 | # 84 | # ``` 85 | # DOTNET_REGISTER_LOCAL_REPOSITORY(repo_name repo_path) 86 | # ``` 87 | # 88 | # TEST_DOTNET -- add a dotnet test project to ctest. The project will be run with `dotnet test`, 89 | # and trx test reports will be generated in the build directory. For Windows, all target frameworks 90 | # are tested against. For other platforms, only .NET Core App is tested against. 91 | # Test failures will not fail the build. 92 | # Tests are only run with `ctest -C `, not with `cmake --build ...` 93 | # 94 | # ``` 95 | # TEST_DOTNET( 96 | # [ARGUMENTS additional_dotnet_test_args...] 97 | # [OUTPUT_PATH output_path relative to cmake binary output dir]) 98 | # ``` 99 | # 100 | # GEN_DOTNET_PROPS -- Generates a Directory.Build.props file. The created file is populated with MSBuild properties: 101 | # - DOTNET_PACKAGE_VERSION: a version string that can be referenced in the actual project file as $(DOTNET_PACKAGE_VERSION). 102 | # The version string value can be set with PACKAGE_VERSION argument, and defaults to '1.0.0'. 103 | # - XPLAT_LIB_DIR: points to the cmake build root directory. 104 | # - OutputPath: Points to the cmake binary directory (overridden by OUTPUT_PATH, relatively). Therefore, projects built without cmake will consistently output 105 | # to the cmake build directory. 106 | # - Custom properties can be injected with XML_INJECT argument, which injects an arbitrary string into the project XML file. 107 | # 108 | # ``` 109 | # GEN_DOTNET_PROPS( 110 | # [PACKAGE_VERSION version] 111 | # [XML_INJECT xml_injection]) 112 | # ``` 113 | # 114 | # Require 3.5 for batch copy multiple files 115 | 116 | ## commented to silence CMake deprecation warning (< 3.10). 117 | ## the including file is expected to check for a sane CMake version anyways 118 | #cmake_minimum_required(VERSION 3.5.0) 119 | 120 | IF(DOTNET_FOUND) 121 | RETURN() 122 | ENDIF() 123 | 124 | SET(NUGET_CACHE_PATH "~/.nuget/packages") 125 | FIND_PROGRAM(DOTNET_EXE dotnet) 126 | SET(DOTNET_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR}) 127 | 128 | IF(NOT DOTNET_EXE) 129 | SET(DOTNET_FOUND FALSE) 130 | IF(Dotnet_FIND_REQUIRED) 131 | MESSAGE(SEND_ERROR "Command 'dotnet' is not found.") 132 | ENDIF() 133 | RETURN() 134 | ENDIF() 135 | 136 | EXECUTE_PROCESS( 137 | COMMAND ${DOTNET_EXE} --version 138 | OUTPUT_VARIABLE DOTNET_VERSION 139 | OUTPUT_STRIP_TRAILING_WHITESPACE 140 | ) 141 | 142 | #IF(WIN32) 143 | # FIND_PROGRAM(NUGET_EXE nuget PATHS ${CMAKE_BINARY_DIR}/tools) 144 | # IF(NUGET_EXE) 145 | # MESSAGE("-- Found nuget: ${NUGET_EXE}") 146 | # ELSE() 147 | # SET(NUGET_EXE ${CMAKE_BINARY_DIR}/tools/nuget.exe) 148 | # MESSAGE("-- Downloading nuget...") 149 | # FILE(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${NUGET_EXE}) 150 | # MESSAGE("nuget.exe downloaded and saved to ${NUGET_EXE}") 151 | # ENDIF() 152 | #ENDIF() 153 | 154 | FUNCTION(DOTNET_REGISTER_LOCAL_REPOSITORY repo_name repo_path) 155 | MESSAGE("-- Registering NuGet local repository '${repo_name}' at '${repo_path}'.") 156 | GET_FILENAME_COMPONENT(repo_path ${repo_path} ABSOLUTE) 157 | IF(WIN32) 158 | STRING(REPLACE "/" "\\" repo_path ${repo_path}) 159 | EXECUTE_PROCESS(COMMAND ${NUGET_EXE} sources list OUTPUT_QUIET) 160 | EXECUTE_PROCESS(COMMAND ${NUGET_EXE} sources Remove -Name "${repo_name}" OUTPUT_QUIET ERROR_QUIET) 161 | EXECUTE_PROCESS(COMMAND ${NUGET_EXE} sources Add -Name "${repo_name}" -Source "${repo_path}") 162 | ELSE() 163 | GET_FILENAME_COMPONENT(nuget_config ~/.nuget/NuGet/NuGet.Config ABSOLUTE) 164 | EXECUTE_PROCESS(COMMAND ${DOTNET_EXE} nuget locals all --list OUTPUT_QUIET) 165 | EXECUTE_PROCESS(COMMAND sed -i "/${repo_name}/d" "${nuget_config}") 166 | EXECUTE_PROCESS(COMMAND sed -i "s## \\n #g" "${nuget_config}") 167 | ENDIF() 168 | ENDFUNCTION() 169 | 170 | FUNCTION(DOTNET_GET_DEPS _DN_PROJECT arguments) 171 | CMAKE_PARSE_ARGUMENTS( 172 | # prefix 173 | _DN 174 | # options (flags) 175 | "RELEASE;DEBUG;X86;X64;ANYCPU;NETCOREAPP;NO_RESTORE;NO_CLEAN" 176 | # oneValueArgs 177 | "CONFIG;PLATFORM;VERSION;OUTPUT_PATH;TARGET_NAME" 178 | # multiValueArgs 179 | "PACKAGE;DEPENDS;ARGUMENTS;PACK_ARGUMENTS;OUTPUT;SOURCES;CUSTOM_BUILDPROPS" 180 | # the input arguments 181 | ${arguments}) 182 | 183 | GET_FILENAME_COMPONENT(_DN_abs_proj "${_DN_PROJECT}" ABSOLUTE) 184 | GET_FILENAME_COMPONENT(_DN_proj_dir "${_DN_abs_proj}" DIRECTORY) 185 | GET_FILENAME_COMPONENT(_DN_projname "${_DN_PROJECT}" NAME) 186 | STRING(REGEX REPLACE "\\.[^.]*$" "" _DN_projname_noext ${_DN_projname}) 187 | 188 | FILE(GLOB_RECURSE DOTNET_deps 189 | ${_DN_proj_dir}/*.cs 190 | ${_DN_proj_dir}/*.fs 191 | ${_DN_proj_dir}/*.vb 192 | ${_DN_proj_dir}/*.xaml 193 | ${_DN_proj_dir}/*.resx 194 | ${_DN_proj_dir}/*.xml 195 | ${_DN_proj_dir}/*.*proj 196 | ${_DN_proj_dir}/*.cs 197 | ${_DN_proj_dir}/*.config) 198 | LIST(APPEND DOTNET_deps ${_DN_SOURCES}) 199 | SET(_DN_deps "") 200 | FOREACH(dep ${DOTNET_deps}) 201 | IF(NOT dep MATCHES /obj/ AND NOT dep MATCHES /bin/) 202 | LIST(APPEND _DN_deps ${dep}) 203 | ENDIF() 204 | ENDFOREACH() 205 | 206 | 207 | IF(_DN_RELEASE) 208 | SET(_DN_CONFIG Release) 209 | ELSEIF(_DN_DEBUG) 210 | SET(_DN_CONFIG Debug) 211 | ENDIF() 212 | 213 | IF(NOT _DN_CONFIG) 214 | SET(_DN_CONFIG "$<$:Debug>$<$>:Release>") 215 | ENDIF() 216 | 217 | # If platform is not specified, do not pass the Platform property. 218 | # dotnet will pick the default Platform. 219 | 220 | IF(_DN_X86) 221 | SET(_DN_PLATFORM x86) 222 | ELSEIF(_DN_X64) 223 | SET(_DN_PLATFORM x64) 224 | ELSEIF(_DN_ANYCPU) 225 | SET(_DN_PLATFORM "AnyCPU") 226 | ENDIF() 227 | 228 | # If package version is not set, first fallback to DOTNET_PACKAGE_VERSION 229 | # If again not set, defaults to 1.0.0 230 | IF(NOT _DN_VERSION) 231 | SET(_DN_VERSION ${DOTNET_PACKAGE_VERSION}) 232 | ENDIF() 233 | IF(NOT _DN_VERSION) 234 | SET(_DN_VERSION "1.0.0") 235 | ENDIF() 236 | 237 | # Set the output path to the binary directory. 238 | # Build outputs in separated output directories prevent overwriting. 239 | # Later we then copy the outputs to the destination. 240 | 241 | IF(NOT _DN_OUTPUT_PATH) 242 | SET(_DN_OUTPUT_PATH ${_DN_projname_noext}) 243 | ENDIF() 244 | 245 | GET_FILENAME_COMPONENT(_DN_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${_DN_OUTPUT_PATH} ABSOLUTE) 246 | 247 | if(NOT _DN_TARGET_NAME) 248 | set(_DN_TARGET_NAME ${_DN_projname_noext}) 249 | endif() 250 | 251 | # In a cmake build, the XPLAT libraries are always copied over. 252 | # Set the proper directory for .NET projects. 253 | SET(_DN_XPLAT_LIB_DIR ${CMAKE_BINARY_DIR}) 254 | 255 | set(DOTNET_NO_CLEAN ${_DN_NO_CLEAN} PARENT_SCOPE) 256 | set(DOTNET_NO_RESTORE ${_DN_NO_RESTORE} PARENT_SCOPE) 257 | SET(DOTNET_PACKAGES ${_DN_PACKAGE} PARENT_SCOPE) 258 | SET(DOTNET_CONFIG ${_DN_CONFIG} PARENT_SCOPE) 259 | SET(DOTNET_PLATFORM ${_DN_PLATFORM} PARENT_SCOPE) 260 | SET(DOTNET_DEPENDS ${_DN_DEPENDS} PARENT_SCOPE) 261 | SET(DOTNET_PROJNAME ${_DN_projname_noext} PARENT_SCOPE) 262 | set(DOTNET_TARGETNAME ${_DN_TARGET_NAME} PARENT_SCOPE) 263 | SET(DOTNET_PROJPATH ${_DN_abs_proj} PARENT_SCOPE) 264 | SET(DOTNET_PROJDIR ${_DN_proj_dir} PARENT_SCOPE) 265 | SET(DOTNET_ARGUMENTS ${_DN_ARGUMENTS} PARENT_SCOPE) 266 | SET(DOTNET_RUN_OUTPUT ${_DN_OUTPUT} PARENT_SCOPE) 267 | SET(DOTNET_PACKAGE_VERSION ${_DN_VERSION} PARENT_SCOPE) 268 | SET(DOTNET_OUTPUT_PATH ${_DN_OUTPUT_PATH} PARENT_SCOPE) 269 | SET(DOTNET_deps ${_DN_deps} PARENT_SCOPE) 270 | 271 | IF(_DN_PLATFORM) 272 | SET(_DN_PLATFORM_PROP "/p:Platform=${_DN_PLATFORM}") 273 | ENDIF() 274 | 275 | IF(_DN_NETCOREAPP) 276 | SET(_DN_BUILD_OPTIONS -f net8.0) 277 | SET(_DN_PACK_OPTIONS /p:TargetFrameworks=net8.0) 278 | ELSEIF(UNIX) 279 | # Unix builds default to net8.0 280 | SET(_DN_BUILD_OPTIONS -f net8.0) 281 | SET(_DN_PACK_OPTIONS /p:TargetFrameworks=net8.0) 282 | ENDIF() 283 | 284 | SET(_DN_IMPORT_PROP ${CMAKE_CURRENT_BINARY_DIR}/${_DN_projname}.imports.props) 285 | CONFIGURE_FILE(${DOTNET_MODULE_DIR}/DotnetImports.props.in ${_DN_IMPORT_PROP}) 286 | 287 | SET(_DN_IMPORT_ARGS "/p:DirectoryBuildPropsPath=${_DN_IMPORT_PROP}") 288 | 289 | SET(DOTNET_IMPORT_PROPERTIES ${_DN_IMPORT_ARGS} PARENT_SCOPE) 290 | SET(DOTNET_BUILD_PROPERTIES ${_DN_PLATFORM_PROP} ${_DN_IMPORT_ARGS} PARENT_SCOPE) 291 | SET(DOTNET_BUILD_OPTIONS ${_DN_BUILD_OPTIONS} PARENT_SCOPE) 292 | SET(DOTNET_PACK_OPTIONS --include-symbols ${_DN_PACK_OPTIONS} ${_DN_PACK_ARGUMENTS} PARENT_SCOPE) 293 | 294 | ENDFUNCTION() 295 | 296 | MACRO(ADD_DOTNET_DEPENDENCY_TARGETS tgt) 297 | FOREACH(pkg_dep ${DOTNET_DEPENDS}) 298 | ADD_DEPENDENCIES(${tgt}_${DOTNET_PROJNAME} PKG_${pkg_dep}) 299 | MESSAGE(" ${DOTNET_PROJNAME} <- ${pkg_dep}") 300 | ENDFOREACH() 301 | 302 | FOREACH(pkg ${DOTNET_PACKAGES}) 303 | STRING(TOLOWER ${pkg} pkg_lowercase) 304 | GET_FILENAME_COMPONENT(cache_path ${NUGET_CACHE_PATH}/${pkg_lowercase} ABSOLUTE) 305 | IF(WIN32) 306 | SET(rm_command powershell -NoLogo -NoProfile -NonInteractive -Command "Remove-Item -Recurse -Force -ErrorAction Ignore '${cache_path}'\; exit 0") 307 | ELSE() 308 | SET(rm_command rm -rf ${cache_path}) 309 | ENDIF() 310 | ADD_CUSTOM_TARGET( 311 | DOTNET_PURGE_${pkg} 312 | COMMAND ${CMAKE_COMMAND} -E echo "======= [x] Purging nuget package cache for ${pkg}" 313 | COMMAND ${rm_command} 314 | DEPENDS ${DOTNET_deps} 315 | ) 316 | ADD_DEPENDENCIES(${tgt}_${DOTNET_PROJNAME} DOTNET_PURGE_${pkg}) 317 | # Add a target for the built package -- this can be referenced in 318 | # another project. 319 | ADD_CUSTOM_TARGET(PKG_${pkg}) 320 | ADD_DEPENDENCIES(PKG_${pkg} ${tgt}_${DOTNET_PROJNAME}) 321 | MESSAGE("==== ${DOTNET_PROJNAME} -> ${pkg}") 322 | ENDFOREACH() 323 | ENDMACRO() 324 | 325 | MACRO(DOTNET_BUILD_COMMANDS) 326 | IF(${DOTNET_IS_MSBUILD}) 327 | SET(build_dotnet_cmds 328 | COMMAND ${CMAKE_COMMAND} -E echo "======= Building msbuild project ${DOTNET_PROJNAME} [${DOTNET_CONFIG} ${DOTNET_PLATFORM}]" 329 | ) 330 | 331 | if(NOT DOTNET_NO_RESTORE) 332 | list(APPEND build_dotnet_cmds 333 | COMMAND ${NUGET_EXE} restore -Force ${DOTNET_PROJPATH} 334 | ) 335 | endif() 336 | if(NOT DOTNET_NO_CLEAN) 337 | list(APPEND build_dotnet_cmds 338 | COMMAND ${DOTNET_EXE} msbuild ${DOTNET_PROJPATH} /t:Clean ${DOTNET_BUILD_PROPERTIES} /p:Configuration="${DOTNET_CONFIG}" 339 | ) 340 | endif() 341 | 342 | list(APPEND build_dotnet_cmds 343 | COMMAND ${DOTNET_EXE} msbuild ${DOTNET_PROJPATH} /t:Build ${DOTNET_BUILD_PROPERTIES} /p:Configuration="${DOTNET_CONFIG}" ${DOTNET_ARGUMENTS} 344 | ) 345 | 346 | 347 | SET(build_dotnet_type "msbuild") 348 | ELSE() 349 | SET(build_dotnet_cmds 350 | COMMAND ${CMAKE_COMMAND} -E echo "======= Building .NET project ${DOTNET_PROJNAME} [${DOTNET_CONFIG} ${DOTNET_PLATFORM}]" 351 | ) 352 | 353 | set(restore_arg "") 354 | if(NOT DOTNET_NO_RESTORE) 355 | list(APPEND build_dotnet_cmds 356 | COMMAND ${DOTNET_EXE} restore ${DOTNET_PROJPATH} ${DOTNET_IMPORT_PROPERTIES} 357 | ) 358 | set(restore_arg "--no-restore") 359 | endif() 360 | 361 | if(NOT DOTNET_NO_CLEAN) 362 | list(APPEND build_dotnet_cmds 363 | COMMAND ${DOTNET_EXE} clean ${DOTNET_PROJPATH} ${DOTNET_BUILD_PROPERTIES} 364 | ) 365 | endif() 366 | 367 | list(APPEND build_dotnet_cmds 368 | COMMAND ${DOTNET_EXE} build ${restore_arg} ${DOTNET_PROJPATH} -c ${DOTNET_CONFIG} ${DOTNET_BUILD_PROPERTIES} ${DOTNET_BUILD_OPTIONS} ${DOTNET_ARGUMENTS} 369 | ) 370 | 371 | SET(build_dotnet_type "dotnet") 372 | ENDIF() 373 | 374 | # DOTNET_OUTPUTS refer to artifacts produced, that the BUILD_proj_name target depends on. 375 | SET(DOTNET_OUTPUTS "") 376 | IF(NOT "${DOTNET_PACKAGES}" STREQUAL "") 377 | MESSAGE("-- Adding ${build_dotnet_type} project ${DOTNET_PROJPATH} (version ${DOTNET_PACKAGE_VERSION})") 378 | FOREACH(pkg ${DOTNET_PACKAGES}) 379 | LIST(APPEND DOTNET_OUTPUTS ${DOTNET_OUTPUT_PATH}/${pkg}.${DOTNET_PACKAGE_VERSION}.nupkg) 380 | LIST(APPEND DOTNET_OUTPUTS ${DOTNET_OUTPUT_PATH}/${pkg}.${DOTNET_PACKAGE_VERSION}.symbols.nupkg) 381 | LIST(APPEND build_dotnet_cmds COMMAND ${CMAKE_COMMAND} -E remove ${DOTNET_OUTPUT_PATH}/${pkg}.${DOTNET_PACKAGE_VERSION}.nupkg) 382 | LIST(APPEND build_dotnet_cmds COMMAND ${CMAKE_COMMAND} -E remove ${DOTNET_OUTPUT_PATH}/${pkg}.${DOTNET_PACKAGE_VERSION}.symbols.nupkg) 383 | ENDFOREACH() 384 | LIST(APPEND build_dotnet_cmds COMMAND ${DOTNET_EXE} pack --no-build --no-restore ${DOTNET_PROJPATH} -c ${DOTNET_CONFIG} ${DOTNET_BUILD_PROPERTIES} ${DOTNET_PACK_OPTIONS}) 385 | LIST(APPEND build_dotnet_cmds COMMAND ${CMAKE_COMMAND} -E copy ${DOTNET_OUTPUTS} ${CMAKE_BINARY_DIR}) 386 | ELSE() 387 | MESSAGE("-- Adding ${build_dotnet_type} project ${DOTNET_PROJPATH} (no nupkg)") 388 | ENDIF() 389 | LIST(APPEND DOTNET_OUTPUTS ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_PROJNAME}.buildtimestamp) 390 | LIST(APPEND build_dotnet_cmds COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_PROJNAME}.buildtimestamp) 391 | 392 | ADD_CUSTOM_COMMAND( 393 | OUTPUT ${DOTNET_OUTPUTS} 394 | DEPENDS ${DOTNET_deps} 395 | ${build_dotnet_cmds} 396 | ) 397 | 398 | ADD_CUSTOM_TARGET( 399 | ${DOTNET_TARGETNAME} ALL 400 | DEPENDS ${DOTNET_OUTPUTS}) 401 | 402 | ENDMACRO() 403 | 404 | FUNCTION(ADD_DOTNET DOTNET_PROJECT) 405 | DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN}") 406 | SET(DOTNET_IS_MSBUILD FALSE) 407 | DOTNET_BUILD_COMMANDS() 408 | ADD_DOTNET_DEPENDENCY_TARGETS(BUILD) 409 | ENDFUNCTION() 410 | 411 | FUNCTION(ADD_MSBUILD DOTNET_PROJECT) 412 | IF(NOT WIN32) 413 | MESSAGE("-- Building non-Win32, skipping ${DOTNET_PROJECT}") 414 | RETURN() 415 | ENDIF() 416 | 417 | DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN}") 418 | SET(DOTNET_IS_MSBUILD TRUE) 419 | DOTNET_BUILD_COMMANDS() 420 | ADD_DOTNET_DEPENDENCY_TARGETS(BUILD) 421 | ENDFUNCTION() 422 | 423 | FUNCTION(RUN_DOTNET DOTNET_PROJECT) 424 | DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN};NETCOREAPP") 425 | MESSAGE("-- Adding dotnet run project ${DOTNET_PROJECT}") 426 | FILE(MAKE_DIRECTORY ${DOTNET_OUTPUT_PATH}) 427 | 428 | set(dotnet_run_cmds "") 429 | set(dotnet_restore_arg "") 430 | 431 | if(NOT DOTNET_NO_RESTORE) 432 | list(APPEND dotnet_run_cmds 433 | COMMAND ${DOTNET_EXE} restore ${DOTNET_PROJPATH} ${DOTNET_IMPORT_PROPERTIES} 434 | ) 435 | set(dotnet_restore_arg "--no-restore") 436 | endif() 437 | 438 | if(NOT DOTNET_NO_CLEAN) 439 | list(APPEND dotnet_run_cmds 440 | COMMAND ${DOTNET_EXE} clean ${DOTNET_PROJPATH} ${DOTNET_BUILD_PROPERTIES} 441 | ) 442 | endif() 443 | 444 | list(APPEND dotnet_run_cmds 445 | COMMAND ${DOTNET_EXE} build ${dotnet_restore_arg} ${DOTNET_PROJPATH} -c ${DOTNET_CONFIG} ${DOTNET_BUILD_PROPERTIES} ${DOTNET_BUILD_OPTIONS} 446 | ) 447 | 448 | set(aio_run_command 449 | ${DOTNET_EXE} run -p ${DOTNET_PROJPATH} -c ${DOTNET_CONFIG} ${DOTNET_BUILD_OPTIONS} -- ${DOTNET_ARGUMENTS} 450 | ) 451 | 452 | ADD_CUSTOM_COMMAND( 453 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_TARGETNAME}.runtimestamp ${DOTNET_RUN_OUTPUT} 454 | DEPENDS ${DOTNET_deps} 455 | ${dotnet_run_cmds} 456 | # XXX tfm 457 | COMMAND ${DOTNET_EXE} ${DOTNET_OUTPUT_PATH}/net8.0/${DOTNET_PROJNAME}.dll ${DOTNET_ARGUMENTS} 458 | #COMMAND ${CMAKE_COMMAND} -E echo ${aio_run_command} 459 | #COMMAND ${aio_run_command} 460 | COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_TARGETNAME}.runtimestamp 461 | WORKING_DIRECTORY ${DOTNET_OUTPUT_PATH}) 462 | ADD_CUSTOM_TARGET( 463 | ${DOTNET_TARGETNAME} 464 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_TARGETNAME}.runtimestamp ${DOTNET_RUN_OUTPUT}) 465 | ADD_DOTNET_DEPENDENCY_TARGETS(RUN) 466 | ENDFUNCTION() 467 | 468 | FUNCTION(TEST_DOTNET DOTNET_PROJECT) 469 | DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN}") 470 | MESSAGE("-- Adding dotnet test project ${DOTNET_PROJECT}") 471 | IF(WIN32) 472 | SET(test_framework_args "") 473 | ELSE() 474 | SET(test_framework_args -f net8.0) 475 | ENDIF() 476 | 477 | ADD_TEST(NAME ${DOTNET_PROJNAME} 478 | COMMAND ${DOTNET_EXE} test ${DOTNET_PROJECT} ${test_framework_args} --results-directory "${CMAKE_BINARY_DIR}" --logger trx ${DOTNET_ARGUMENTS}) 479 | 480 | ENDFUNCTION() 481 | 482 | SET_PROPERTY(GLOBAL PROPERTY DOTNET_LAST_SMOKETEST "") 483 | 484 | FUNCTION(SMOKETEST_DOTNET DOTNET_PROJECT) 485 | MESSAGE("-- Adding dotnet smoke test project ${DOTNET_PROJECT}") 486 | IF(WIN32) 487 | RUN_DOTNET(${DOTNET_PROJECT} "${ARGN}") 488 | ELSE() 489 | RUN_DOTNET(${DOTNET_PROJECT} "${ARGN}") 490 | ENDIF() 491 | 492 | DOTNET_GET_DEPS(${DOTNET_PROJECT} "${ARGN}") 493 | ADD_CUSTOM_TARGET( 494 | SMOKETEST_${DOTNET_PROJNAME} 495 | ALL 496 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_TARGETNAME}.runtimestamp) 497 | ADD_DOTNET_DEPENDENCY_TARGETS(SMOKETEST) 498 | GET_PROPERTY(_dn_last_smoketest GLOBAL PROPERTY DOTNET_LAST_SMOKETEST) 499 | IF(_dn_last_smoketest) 500 | MESSAGE("${_dn_last_smoketest} -> SMOKETEST_${DOTNET_PROJNAME}") 501 | ADD_DEPENDENCIES(SMOKETEST_${DOTNET_PROJNAME} ${_dn_last_smoketest}) 502 | ENDIF() 503 | # Chain the smoke tests together so they are executed sequentially 504 | SET_PROPERTY(GLOBAL PROPERTY DOTNET_LAST_SMOKETEST SMOKETEST_${DOTNET_PROJNAME}) 505 | 506 | ENDFUNCTION() 507 | 508 | SET(DOTNET_IMPORTS_TEMPLATE ${CMAKE_CURRENT_LIST_DIR}/DotnetImports.props.in) 509 | 510 | FUNCTION(GEN_DOTNET_PROPS target_props_file) 511 | CMAKE_PARSE_ARGUMENTS( 512 | # prefix 513 | _DNP 514 | # options (flags) 515 | "" 516 | # oneValueArgs 517 | "PACKAGE_VERSION;XML_INJECT" 518 | # multiValueArgs 519 | "" 520 | # the input arguments 521 | ${ARGN}) 522 | 523 | IF(NOT _DNP_PACKAGE_VERSION) 524 | SET(_DNP_PACKAGE_VERSION 1.0.0) 525 | ENDIF() 526 | 527 | IF(_DNP_XML_INJECT) 528 | SET(_DN_CUSTOM_BUILDPROPS ${_DNP_XML_INJECT}) 529 | ENDIF() 530 | 531 | SET(_DN_OUTPUT_PATH ${CMAKE_BINARY_DIR}) 532 | SET(_DN_XPLAT_LIB_DIR ${CMAKE_BINARY_DIR}) 533 | SET(_DN_VERSION ${_DNP_PACKAGE_VERSION}) 534 | CONFIGURE_FILE(${DOTNET_IMPORTS_TEMPLATE} ${target_props_file}) 535 | UNSET(_DN_OUTPUT_PATH) 536 | UNSET(_DN_XPLAT_LIB_DIR) 537 | UNSET(_DN_VERSION) 538 | ENDFUNCTION() 539 | 540 | 541 | MESSAGE("-- Found .NET toolchain: ${DOTNET_EXE} (version ${DOTNET_VERSION})") 542 | SET(DOTNET_FOUND TRUE) 543 | --------------------------------------------------------------------------------