Skip to content

About

A RAM-resident execution and bring-up framework for Qualcomm MSM6290 hardware, enabling custom bootloader staging and Linux kernel development without modifying NAND.

Resources

Stars

4 stars

Watchers

1 watching

Forks

Repository files navigation

Shadow-MSM

License: GPL v3 Status: Halted Target: MSM6290 CPU: ARMv5TEJ

RAM-first bare-metal bring-up and Linux boot research for legacy Qualcomm MSM hardware.

ZTE Vodafone K3765-Z

Shadow-MSM currently targets the ZTE/Vodafone K3765-Z, built around the Qualcomm MSM6290. The project provides a reproducible path from the legacy Qualcomm primary downloader to custom ARM code, a diagnostic stage-0 monitor, and a small second-stage bootloader—without modifying NAND.

Important

Linux v6.1 now executes on the physical K3765-Z from a RAM-only direct Image handoff. The MSM6290 timer/IRQ driver and bidirectional resident-USB bridge are verified on physical hardware: Linux reaches a static BusyBox 1.36.1 ash running as PID 1, accepts ordinary Linux commands, and supports later host reattachment without accessing NAND. The final image also detects a 64 GB SDXC card, mounts its exFAT filesystem, completes a reversible file write/delete test, and runs GNU Bash 5.2.37 with upstream Neofetch 7.1.0.

Why this exists

The MSM6290 predates modern Sahara and Firehose workflows. Public documentation and mainline Linux support are scarce, while vendor tools assume a Windows flashing environment and provide little visibility into the boot process.

Shadow-MSM takes a deliberately conservative approach:

  • execute experimental code from SDRAM;
  • keep the original boot chain and NAND contents intact;
  • verify every uploaded image with a target-side CRC before calling it;
  • log every command, address, result, and observed reset;
  • establish observable hardware checkpoints before attempting a kernel handoff.

Verified target (P673A1, v2?/Hynix unit)

Component Identification
Device Black ZTE/Vodafone K3765-Z
Firmware family BD_VDFP673A1V1.0.0B04
SoC Qualcomm MSM6290
CPU ARM926EJ-S r0p5, ARMv5TEJ
CPU MIDR 0x41069265
RAM 32 MiB address window
NAND Hynix H8ACS0PL0MCR
NAND geometry 128 MiB data + 4 MiB OOB
PMIC family Qualcomm PM6658
Stock runtime Qualcomm AMSS over OKL4/Quartz

Second verified target (P673A1, v1?/Hynix unit)

Component Identification
Device White ZTE/Vodafone K3765-Z
Firmware family BD_VDFP673A1V1.0.0B02 force updated to (IT) BD_VDFP673A1V1.0.0B04
SoC Qualcomm MSM6290
CPU ARM926EJ-S (?), ARMv5TEJ
CPU MIDR 0x41069265?
RAM 32 MiB address window?
NAND Hynix H8ACS0PL0ACR
NAND geometry ?
PMIC family Qualcomm PM6653
Stock runtime Qualcomm AMSS over OKL4/Quartz

Note

The second target is interesting, it seems to be running an older firmware version from another region. RAM, NAND and CPU revision are still unknown since the second unit refuses to go into download. It is running "HR" firmware instead of the previous "SG" firmware of the first unit.

The modem acknowledges DIAG_DLOAD_F but doesn't switch modes, this seems to be a firmware limitation or bug. QPST Software Download gives a generic "Couldn't Change Phone to Download Mode" error. Comparing the dumped EFS of the new unit with the old unit reveals some discrepancies that can be unit specific but that has not been verified yet.

The new downloader workflow is a bit more complex, this might be to a different revision? I cannot verify this claim unfortunately but it may be very possible. The second target is an older revision, this may explain the extra hoops for download mode. It uses an older compatible PMIC but is incompatible with the first target's Linux image due to differences as observed in this log.

Putting an SD Card into the unit fixes the issue, I am not sure why yet.

flowchart TD
    A["K3765-Z<br/>VID:19D2 PID:2002<br/>Normal mode"] --> B["4B 04 0E 00<br/>Liveness check"]
    B --> C["41<br/>Put modem into offline preparation"]
    
    C --> D["4B A3 05 00 + 128-byte AMSS block<br/>Vendor download check"]
    
    D --> E["29 01 00<br/>Put Phone Offline"]
    E -->|"success"| F["4B 04 0E 00<br/>Poll offline status"]
    E -->|"failure ×3"| G["Close diagnostic port<br/>Reopen"]
    G --> H["Wait ~8 seconds"]
    H --> I["29 01 00<br/>Retry after reopen"]
    I --> F
    
    F -->|"status = 0"| J["3A<br/>Switch to downloader"]
    
    J --> K["USB re-enumeration"]
    
    K --> L["VID:19D2 PID:0016"]
    L --> M["ZTE WCDMA Technologies MSM"]
    
Loading

The modem successfully went into download using this flow.


video of the modem booting* Video of the modem booting

The stock AMSS image maps physical memory from 0x00100000 through approximately 0x01F5A000. Shadow-MSM has also executed with a private stack at 0x01FFF000, confirming usable RAM near the top of the 32 MiB window.

Known hardware variants

K3765-Z units were not all assembled with the same memory MCP. An internal photograph from the FCC filing for the K3765-Z shows a Toshiba package marked TY8000A, rather than the Hynix H8ACS0PL0MCR fitted to the physical P673A1 unit used throughout this project. The FCC SAR report identifies its tested sample as hardware P673A1-2.0.0 with firmware BD_VDFP673A1V1.0.0B02. The Toshiba device is therefore evidence of multiple memory configurations within the P673A1 hardware family, not evidence of a P673A2 revision. The two FCC applications cover the Part 15B peripheral and licensed cellular transmitter portions of the same composite device; their separate grant rows do not represent separate board variants.

The closest catalogued full Toshiba part number is TY8000A800AFGP10, an FBGA137 device. Contemporary Qualcomm-platform documentation describes an FBGA137 memory MCP with 1 Gbit NAND and 512 Mbit mobile SDRAM, making 128 MiB NAND + 64 MiB RAM a plausible configuration for the Toshiba variant. The low-resolution package photograph does not expose the complete ordering suffix, however, so those capacities remain unverified until a physical Toshiba unit is identified at runtime.

A complete armprg NAND list can be found here

Variant Memory MCP RAM NAND Status
P673A1 test unit #2 Hynix H8ACS0PL0ACR 32 MiB 128 MiB + 4 MiB OOB? Hardware verified
P673A1 test unit Hynix H8ACS0PL0MCR 32 MiB 128 MiB + 4 MiB OOB Hardware verified
FCC P673A1 sample (B02) Toshiba TY8000A family Possibly 64 MiB Possibly 128 MiB Hardware family confirmed; capacities inferred

Do not apply the Hynix NAND ID, ECC, spare-area, bad-block, or SDRAM assumptions to a Toshiba unit. Shadow-MSM's RAM-only entry mechanism may transfer, but a new unit must first pass non-destructive memory-boundary and hardware-ID probes. The Toshiba configuration has not yet been tested by this project.

Sources: K3765-Z FCC filing, FCC SAR report identifying P673A1/B02, Toshiba programmer-device listing, and a contemporary Qualcomm-platform FBGA137 BOM.

Board photographs

The media/ directory contains high-resolution photographs of the disassembled K3765-Z used for this work. They document both sides of the main PCB and both sides of the detachable SIM/microSD daughterboard. Click any image below for the original file.

Main PCB: RF/component side Main PCB: MSM6290 and Hynix memory side
K3765-Z main PCB RF and component side K3765-Z main PCB MSM6290 and Hynix side
Daughterboard: microSD side Daughterboard: SIM and power side
K3765-Z daughterboard microSD side K3765-Z daughterboard SIM and power side

Current achievements

  • Extracted and mapped the exact OEM firmware package
  • Recovered the matching legacy ARM programmer
  • Entered the pre-Sahara downloader without performing an upgrade
  • Verified legacy PBL RAM-write command 0x0F
  • Verified PBL execute command 0x05
  • Executed custom ARMv5 code entirely from RAM
  • Established a stable USB diagnostic stage-0 monitor
  • Added runtime CP15/system-register queries
  • Added bounded target-side CRC32 verification
  • Added bounded second-stage calls with controlled r0–r2
  • Identified and controlled all RGB LED channels
  • Built and hardware-tested BL1 0.1 with boot logs and system information
  • Built BL1 0.2 Linux-handoff validation and a minimal device tree
  • Verified BL1 0.2 dry run on the target
  • Built and independently verified the first Linux v6.1 ARM926 probe
  • Handed a direct Linux Image and DTB to the ARM926 from RAM
  • Preserved post-handoff diagnostic trace output through early kernel boot
  • Reached rest_init(), PID 1 creation, and the first scheduler call
  • Recovered the MSM6290 timer and interrupt-controller register paths
  • Added a Linux v6.1 clocksource, clock-event, and IRQ-domain driver
  • Verified timer/IRQ registration and the first PID 1 context switch
  • Reached PID 1 kernel_init_freeable() on the physical target
  • Completed the generic initcalls and entered built-in ARM userspace
  • Added and physically verified a reattachable RAM-only command shell
  • Physically verified the static BusyBox 1.36.1 initramfs shell
  • Physically verified the ttySHM0 Linux TTY/console bridge
  • Added a polling SDCC0/microSD block driver for bring-up
  • Detected a 64 GB SDXC card and mounted its existing exFAT filesystem
  • Completed a write, sync, remount, checksum, delete, and read-only remount test
  • Ran GNU Bash 5.2.37 and the exact upstream Neofetch 7.1.0 script on target
  • Replace the diagnostic trace path with a normal UART or USB console
  • Add NAND read-only, native USB gadget, and display support

Important

The microSD path is physically verified as a bring-up driver. It detected a SanDisk Ultra Plus 64 GB card, initialized it in 1-bit mode at 400 kHz before switching to 4 MHz, read the existing exFAT filesystem, and completed a reversible file write/delete test. It is deliberately polling-only and is not a production SD/MMC implementation: hot-plug, DMA, high-speed modes, write protection, and broad card compatibility remain unimplemented.

NAND remains untouched. Native USB gadget support and a display stack were not completed before the original board was damaged.

See the final hardware verification report for hashes, observed card geometry, and the exact reversible test boundary.

Boot architecture

flowchart LR
    PBL["Qualcomm Boot ROM / legacy PBL"]
    S0["Shadow-MSM stage-0<br/>0x00800000"]
    BL1["Shadow-MSM BL1<br/>0x01000000"]
    IMAGE["Linux direct Image<br/>0x00208000"]
    DTB["Device tree<br/>0x01F80000"]
    LINUX["Linux v6.1<br/>ARM926 probe"]

    PBL -->|"RAM upload + execute"| S0
    S0 -->|"CRC + bounded call"| BL1
    BL1 -->|"validated non-returning handoff"| IMAGE
    DTB -.-> BL1
    IMAGE --> LINUX
Loading

The stage-0 monitor is based on the initialized OEM ARM programmer runtime so the known USB diagnostic transport remains available. BL1 uses its own stack, prints hardware and CP15 state, and currently returns cleanly to stage-0.

RAM layout

Address range Purpose
0x00100000..0x001FFFFF Conservatively unused low SDRAM
0x00200000..0x0076DFE7 Linux Image, BSS, and built-in userspace runtime
0x0076DFE8..0x0077FFFF Verified space below the direct-image limit
0x00780000..0x007FFFFF 512 KiB guard below the resident stage-0 monitor
0x00800000..0x00819DC7 RAM-only stage-0 monitor
0x01000000 BL1 load and entry
0x01F80000..0x01F8FFFF Reserved DTB window
0x01FFF000 BL1 private stack top
0x02000000 End of the 32 MiB RAM window

See the detailed layout.

Repository layout

Shadow-MSM/
├── README.md
├── LICENSE
├── CHECKSUMS.sha256
├── requirements.txt
├── firmware/
│   └── README.md
├── work/
│   ├── firmware-analysis utilities
│   └── stage-0 / BL1 image builders
├── kernel/
│   ├── Linux v6.1 patch series and reproducible build
│   ├── MSM6290 timer, IRQ, TTY, and SDCC bring-up sources
│   └── pinned upstream userspace sources and provenance
└── outputs/
    ├── RAM-only host tools and generated images
    ├── disassembly and address maps
    ├── BL1_README.md
    ├── BL1_0.2_DRYRUN_README.md
    └── TEST_LOG.md

The complete chronological evidence trail is in outputs/TEST_LOG.md.

Requirements

Modem needs to have the Diagnostic port enabled via DC-Unlocker 2 (optional?)

The current development host is Windows. The tools require:

  • Python 3.9 or newer;
  • pyserial;
  • capstone;
  • keystone-engine;
  • the correct Windows serial driver for the legacy ZTE downloader interface;
  • a legally obtained copy of the matching K3765-Z stock updater.

Install the Python dependencies:

py -3.9 -m pip install pyserial capstone keystone-engine

Prepare the OEM-derived runtime locally

The repository intentionally does not contain armprg.bin or a patched stage-0 image. Extract the matching programmer from a legally obtained stock updater and place it at:

firmware/armprg.bin

The builder verifies its size and SHA-256 before making any output. Build the RAM-only stage-0 monitor with:

py -3.9 .\work\build_armprg_stage0_monitor.py

This produces the locally ignored outputs/armprg_stage0_monitor.bin. See firmware/README.md for the accepted source hash.

Safe quick start: verified BL1 0.1

Start from a fresh legacy downloader session. Replace COMxx with the active downloader port:

py -3.9 .\outputs\k3765_stage0_load.py COMxx `
  .\outputs\armprg_stage0_monitor.bin `
  .\outputs\k3765_stage2_bootloader.bin `
  --log .\outputs\bl1_load.log

Verify the image in target RAM before executing it:

py -3.9 .\outputs\k3765_stage0_console.py COMxx `
  crc 0x01000000 2861

Expected:

CRC32: 0xEFF2DC54

Run BL1:

py -3.9 .\outputs\k3765_stage0_console.py COMxx `
  boot 0x01000000 0xA0A0A0A0 0xB1B1B1B1 0xC2C2C2C2 `
  --log .\outputs\bl1_boot.log

Expected final result:

RETURN R0: 0x424F4F54

0x424F4F54 is ASCII BOOT. After execution, the BL1 CRC changes to 0x83FFF9DF because its 13-byte hexadecimal formatting buffer contains the last printed value, 0xC2C2C2C2; the executable code is unchanged.

Linux-handoff dry run

BL1 0.2 has been verified on the physical K3765-Z. It validates a header-only zImage fixture and a minimal flattened device tree, prints the planned Linux entry registers, and returns DRY1 without jumping.

The complete commands, CRCs, and expected output are documented in outputs/BL1_0.2_DRYRUN_README.md.

Linux probe status

The kernel/ tree contains a reproducible Linux v6.1 ARM926 probe build. It adds pre-MMU and early-kernel trace milestones for the K3765-Z, compiles bounded Image, zImage, DTB, and symbol artifacts in GitHub Actions, and rejects any image that would overlap the resident stage-0 runtime.

The physical target now reaches stable, interactive ARM userspace. Commit 4c5eff8, built by GitHub Actions run 31323294185, boots a pinned, statically linked BusyBox 1.36.1 ARMv5 ash as PID 1. The hardware run reported Linux 6.1.0-shadow-msm-probe+ / armv5tejl, mounted proc, sysfs, and RAM-backed tmpfs, exposed 25,728 KiB of RAM, and successfully ran ordinary BusyBox commands as UID 0.

The host later reattached to the same live kernel without rebooting it; /proc/1/exe still resolved to /bin/busybox after more than four minutes. The monitor accepts host input only through Shadow-MSM command 0x1c/0x0e; its complete live command table is locked to the bounded Shadow-MSM handler so the original storage handlers remain unreachable.

Commit 411b48d, built successfully by GitHub Actions run 31330993698, promotes that byte bridge to a proper Linux TTY named ttySHM0. Linux owns canonical input, echo, signal characters, and /dev/console, while /dev/shadowtrace remains an emergency fallback. The host tools forward individual keys: Ctrl+C is delivered to the foreground Linux process and Ctrl+] detaches the host without rebooting the target. This exact TTY artifact passed compile, hash, RAM-boundary, and physical-hardware verification.

The final physical build adds a polling SDCC0 host. It identified a SanDisk Ultra Plus 64 GB card as SK64G (59.5 GiB), exposed partition p1, mounted the existing exFAT filesystem, read existing files, and completed a bounded create/checksum/delete test. GNU Bash 5.2.37 and the unmodified upstream Neofetch 7.1.0 script then ran from the built-in initramfs. Exact hashes and the physical test record are in outputs/FINAL_HARDWARE_VERIFICATION_20260810.md.

After loading the matching monitor and verified kernel bundle, start Linux with an interactive terminal by adding --interactive to the existing linux command:

py -3.9 .\outputs\k3765_stage0_console.py COMxx linux `
  0x01000000 IMAGE_SIZE DTB_SIZE 0x494D4731 `
  --interactive --idle-timeout 0 `
  --log .\outputs\linux_interactive.log

An already-running interactive build can be reattached without restarting it:

py -3.9 .\outputs\k3765_stage0_console.py COMxx attach `
  --log .\outputs\linux_attach.log

Offline Linux shell kit

outputs/k3765_linux_shell.py integrates payload verification, the normal-to-downloader switch, bounded SDRAM loading, target-side CRC checks, Linux handoff, live boot logs, BusyBox shell I/O, and reattachment in one host program. The companion work/build_linux_shell_iso_kit.py assembles a read-only virtual-CD kit from locally supplied, hash-verified artifacts and dependencies.

The kernel build pins the exact Debian ARMEL static Bash package and embeds the upstream Neofetch source with its license and commit provenance. These are third-party components under their respective licenses; they are not reimplementations or look-alike output.

Vendor firmware, the ZTE driver package, and generated ISO files are not stored in this repository. Building or mounting the kit does not write the modem's NAND; replacing the stock virtual-CD file is a separate, explicitly persistent operation.

This remains a bring-up kernel rather than a complete distribution. It still uses the resident OEM USB transport, and it has no native USB controller, native USB, NAND, cellular, production SD/MMC, or production console driver. Every current boot and shell component is RAM-resident; power-cycling returns to the stock firmware.

SHA-256 values for the redistributable generated artifacts are recorded in CHECKSUMS.sha256.

RGB diagnostic LED

The K3765-Z multicolor LED is useful as an early bring-up console:

Color Control path
Red PMIC MPP current sink 1
Green PMIC LED channel 0
Blue PMIC LED channel 1
Cyan Green + blue
White Red + green + blue

LED checkpoints can remain available after the resident USB transport is discarded during a future non-returning Linux handoff.

Safety model

Shadow-MSM is designed around volatile experimentation:

  • current loaders write only to the bounded SDRAM window;
  • current payloads do not contain NAND erase or program implementations;
  • removable-card writes occur only when explicitly requested from Linux userspace;
  • BL1 does not link or call the OEM NAND routines;
  • power-cycling removes the experimental code and restores the stock boot path;
  • target-side CRC verification is required before a second-stage call.

This is experimental low-level software. A coding error can still hang or reset the target. Use only hardware you own, preserve the original updater and backups, and inspect every command before running it.

Do not substitute QPST's User Partitions workflow for these RAM-only commands. In Qualcomm terminology that screen performs host-to-device writes.

Firmware and redistribution

Shadow-MSM does not grant permission to redistribute Qualcomm, ZTE, Vodafone, or other vendor firmware. Stock updaters, firmware images, identifiers, and device dumps remain subject to their original terms.

Keep vendor files outside the public repository. Prefer build scripts that patch a user-supplied, hash-verified stock image locally instead of committing vendor-derived binaries.

Qualcomm, ZTE, Vodafone, Hynix, ARM, and other names are trademarks of their respective owners. Shadow-MSM is an independent research project and is not affiliated with or endorsed by those companies.

Contributing

Contributions are welcome, especially:

  • confirmed MSM6246/MSM6275/MSM6280/MSM6290 register information;
  • UART pin-mux and test-pad identification;
  • timer, interrupt-controller, clock, GPIO, and (especially) USB documentation;
  • reproducible logs from closely related devices;
  • small ARMv5-compatible Linux drivers;
  • review of cache, MMU, and kernel-handoff code.

Please preserve the RAM-first default, document the exact hardware and firmware revision used, include hashes and target logs, and clearly label anything that has not been tested on physical hardware.

License

The original Shadow-MSM source code and documentation are licensed under the GNU General Public License version 3 only (GPL-3.0-only).

The GPL does not apply to third-party firmware, drivers, trademarks, or dumps merely used with the project. See the GNU GPL version 3 for the complete license terms.

About

A RAM-resident execution and bring-up framework for Qualcomm MSM6290 hardware, enabling custom bootloader staging and Linux kernel development without modifying NAND.

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages