Overrides the Ubuntu 22.04 whitelist check on the 3.1.0 MUSA driver for the MTT S80 GPU, allowing you to run the driver on other operating systems. Tested and fully functional on Deepin 23.
Find a file
2026-06-15 23:21:41 -04:00
Makefile Mon Jun 15 10:57:00 PM EDT 2026 2026-06-15 22:57:00 -04:00
patch.py Mon Jun 15 10:57:00 PM EDT 2026 2026-06-15 22:57:00 -04:00
README.md Update README.md 2026-06-15 23:21:41 -04:00
run.sh Mon Jun 15 10:57:00 PM EDT 2026 2026-06-15 22:57:00 -04:00

Moore Threads mtgpu Driver OS Lock Bypass

This project provides a technical patch for the Moore Threads mtgpu kernel driver to bypass an artificial OS distribution restriction. The proprietary driver refuses to load on unsupported Linux distributions by returning error -524 (ENOTSUPP), even on perfectly compatible hardware and kernel versions.

A pre-built driver is currently available at the following URL: https://www.foleosoft.com/firmware/musa/musa_3.1.0-rc4.2.0-Unlocked_amd64.deb

Usage and Installation

This repository automates the extraction, patching, and repacking of the official Moore Threads driver package. You do not need to manually hex-edit any files.

  1. Clone this repository to your local machine.
  2. Navigate to the official Moore Threads developer portal and download the MUSA SDK 4.2.0 package for the MTT S80. Download URL: https://developer.mthreads.com/sdk/download/musa?equipment=MTT%20S80&os=Ubuntu&driverVersion=&version=4.2.0
  3. Ensure the downloaded file is named exactly MUSA_SDK_4.2.0.CC2.1.zip.
  4. Place the MUSA_SDK_4.2.0.CC2.1.zip file directly into the root folder of this cloned repository.
  5. Open your terminal in the repository folder and type make, then press Enter.

The Makefile will automatically extract the official driver from the SDK file, locate the proprietary binary blob, apply the assembly-level bypass, and repackage everything into a clean, unlocked Debian package.

Once the build process finishes, you can install the unlocked driver on any compatible Linux distribution using standard package management tools:

sudo apt install ./musa_3.1.0-rc4.2.0-Unlocked_amd64.deb

This project has not been tested on SDKs other than 4.2.0. The choice of this SDK is that it currently (as of time of writing) contains the most up-to-date driver for the MTT S80, as well as is the recommended driver for the official PyTorch Docker container, useful if you wish to run AI models on the GPU.

Reverse Engineering Methodology

Bypassing proprietary vendor locks in pre-compiled binary blobs requires a surgical approach. Because the core hardware abstraction logic is hidden inside a closed-source object file (mtgpu.core.o_binary), we cannot simply edit the C source code to remove the restriction. Instead, we must locate the exact machine code instructions responsible for the lockout and neutralize them.

Here is the step-by-step technical breakdown of how the patch location was discovered and verified.

1. Identifying the Error Signature

When the driver is loaded on an unapproved distribution, the initialization fails immediately and prints a specific error to the kernel ring buffer:

mtgpu 0000:01:00.0: Unsupported OS version: #1 SMP Tue Nov 9 20:32:54 CST 2021
mtgpu 0000:01:00.0: mtdev init failed -524
mtgpu: probe of 0000:01:00.0 failed with error -524

This output gives us two critical pieces of information. First, the exact string literal "Unsupported OS version" exists somewhere in the binary. Second, the function returns the integer -524, which corresponds to the Linux kernel error code ENOTSUPP (hexadecimal 0xfffffdfc in 32-bit two's complement).

2. String Hunting and Hex Analysis

The first step in reverse engineering a compiled binary is to locate the plaintext strings used by the program. Using hexadecimal dump tools like xxd and standard text search utilities, we scanned the compiled kernel module and the underlying binary blob for the string "Ubuntu" and the error message "Unsupported".

We successfully located the exact byte offsets where these strings reside in the read-only data section (.rodata) of the ELF file. For example, the string "Ubuntu" was found at a specific file offset, immediately followed by the "Unsupported OS version" string.

However, knowing where the string is stored in the data section is not enough. We need to find the executable code in the text section (.text) that actually reads this string and makes the decision to abort the driver load.

3. Navigating ELF Relocations

Kernel modules (.ko files) and their underlying object files are not fully linked executables. They are relocatable ELF objects. This means the executable instructions in the .text section do not contain hardcoded, absolute memory addresses pointing to the strings in the .rodata section. Instead, the compiler inserts placeholder values and generates a relocation table. When the kernel loads the module, it reads this table and patches the placeholders with the correct runtime memory addresses.

To bridge the gap between our string in the data section and the code in the text section, we had to use the readelf utility to inspect the section headers and the relocation tables.

First, we identified the starting file offset of the .rodata.str1.1 section. By subtracting the section's base offset from our string's absolute file offset, we calculated the string's relative offset within that specific section.

Next, we dumped the relocation table (.rela.text) using readelf -r. We searched this massive table for any relocation entry that referenced our calculated relative offset. This search yielded a direct hit, providing us with the exact virtual address in the .text section where the instruction loading the "Ubuntu" string is located.

4. Targeted Disassembly

With the exact virtual address of the string reference in hand, we used objdump -d to disassemble the binary. Rather than dumping the entire multi-megabyte file, we restricted the disassembly output to a narrow window of memory surrounding our target address.

Analyzing the resulting x86_64 assembly revealed the exact logic the vendor used to enforce the lock:

  ; Load the memory address of the "Ubuntu" string into a register
  lea    <offset>(%rip),%rdi
  
  ; Call a string search function (likely strstr) to check the kernel version
  callq  <strstr@plt>
  
  ; Test the result. If the string was found, the return value (rax) is non-zero.
  test   %rax,%rax
  
  ; THE TRAP: If the string was NOT found (rax is zero), jump to the error block
  je     <error_block_address>
  
  ; ... Success path continues here ...

error_block_address:
  ; Load the "Unsupported OS version" string to pass to the logging function
  lea    <offset>(%rip),%rsi
  callq  <dev_err@plt>
  
  ; Set the return register to -524 (0xfffffdfc)
  mov    $0xfffffdfc,%eax
  
  ; Exit the initialization function
  retq

5. Neutralizing the Conditional Jump

The entire vendor lock mechanism hinges on a single conditional jump instruction: je (Jump if Equal / Jump if Zero). In machine code, this specific jump instruction is represented by the bytes 74 5c (where 74 is the opcode for a short conditional jump, and 5c is the relative offset to the error block).

Rewriting the surrounding assembly logic to cleanly handle alternative OS strings would be incredibly complex and prone to breaking internal driver state. Instead, the most robust and elegant solution is to simply remove the trap entirely.

By overwriting the 2-byte conditional jump (74 5c) with two NOP (No-Operation) instructions (90 90), we instruct the CPU to do nothing and simply proceed to the next instruction.

When the driver runs, it still performs the string search, and it still registers that "Ubuntu" was not found. However, when it reaches the point where it would normally jump to the error block and abort, it hits the NOP instructions instead. The CPU gracefully falls through directly into the success path, completely bypassing the vendor lock and allowing the proprietary blob to initialize the hardware normally.