├── 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 |
--------------------------------------------------------------------------------