Files
diskos/README.md
T
b0hemia e0bc4785e9 diskOS installer: initial public beta
Flashes the diskOS custom UI onto the FiiO Snowsky Disc over Ingenic mask-ROM USB,
building the image from your own stock firmware. Runs from source via install.sh.
2026-08-26 15:26:14 +10:00

18 KiB

diskOS

diskOS is a custom player UI/firmware for the FiiO Snowsky Disc digital audio player (Ingenic X2000). It replaces the stock interface, and ships with an installer (run from source with your own Python; a setup script sets up a local virtual environment) that flashes it onto the device over the chip's mask-ROM USB mode - building the image from your own stock firmware, so no FiiO rootfs is redistributed. The installer tooling in this repo is open source; the diskOS UI itself (payload/mq_ui) currently ships as a binary-only component (see License).

⚠️ Unsupported beta tool - read this. Installing erases and rewrites the device's main root filesystem. Power loss, host sleep, a bad cable, a software defect, an unsupported DRAM/NAND variant, or running it on the wrong X2000 device can leave the player unbootable, lose data, or require hardware recovery. Mask-ROM reflashing recovered the tested unit(s), but recovery is not guaranteed for every unit or every failure. This may void your warranty. Not affiliated with or endorsed by FiiO, Snowsky, or Ingenic. Proceed at your own risk; no warranty or support is promised. Back up your data and keep the installer's saved stock image on separate storage.

🧪 BETA. Validated on the Winbond W63AH6NKB (LPDDR3) DRAM - the chip the Snowsky Disc uses. The Disc's own stock bootloader initializes only this chip, so every Disc that runs stock firmware has it; this is the Disc's DRAM, not one of several variants. We have flashed and booted our own unit(s), but wide field-testing is still ongoing, so treat it as beta. In the unlikely event a future hardware revision ever ships different DRAM, the flash fails safe at memory init (the device stays mask-ROM-recoverable) rather than completing. Report your results.

Documentation

Doc What's in it
docs/HARDWARE.md Live-probed hardware capability map: SoC, the quad CS43131 balanced DACs, display planes, power ICs, wireless, USB, and the stock-vs-hardware gap list.
docs/COMMAND_MAP.md The player IPC command surface: ~221 mq_player tags, reply frames, and the MCU/SPI command set - what you drive to build features.
docs/RE_CATALOGUE.md Reverse-engineering of the mq_player/mq_ui binaries: structure, tables, string maps, network receivers.
NOTICE.md Third-party components and their licenses (GPL usbboot, squashfs-tools, the SPL, etc.).
SPL_SOURCE.md GPL corresponding source + build recipe for the stage-1 DRAM bring-up bootloader.
DEPENDENCY_INVENTORY.md Libraries a self-built onefile would bundle + their obligations (the source release does not bundle these).
docs/PRIVACY.md What the installer and the on-device UI send over the network.
agents/ A project brief you can drop into Claude Code or Codex to work on diskOS with an AI agent.
SECURITY.md / CONTRIBUTING.md How to report vulnerabilities; how to contribute safely.
licenses/ Verbatim license texts for the shipped third-party components.

First-time setup

The installer runs from source with your own Python. A one-time setup script builds a local virtual environment and installs the two Python dependencies into it (nothing is installed system-wide):

./install.sh

You need Python 3.8+. The script also checks for two optional system components and prints the exact package to install if either is missing:

  • Tk / tkinter - only for the graphical installer (the command line works without it). Debian/Ubuntu: sudo apt install python3-tk.
  • libusb-1.0 - to detect the device in mask-ROM mode. Debian/Ubuntu: sudo apt install libusb-1.0-0.

After setup, run everything through ./diskos-installer (it uses the .venv automatically).

Install (graphical)

  1. Launch the graphical installer: ./diskos-installer gui.
  2. Choose Install diskOS, pick your FiiO firmware .zip, and a variant:
    • Public - no always-on root shell (recommended). Debug Mode can still enable SSH on demand from the UI (opt-in, off by default - see below).
    • Dev - adds an always-on USB-serial root shell. ⚠️ This is a passwordless root shell available to anyone with physical USB access, on every boot; it bypasses normal device security. Only use it on a development device you control, never an everyday or untrusted one.
  3. Put the device in mask-ROM: power it off, hold Volume-Down, and plug in USB (the screen stays black - that's correct).
  4. Click Install, tick the acknowledgement, and Begin. Don't disconnect or let the computer sleep during the flash (~60-90 minutes).
  5. When it verifies, power-cycle the device. The UI is embedded in the flashed image, so first boot installs diskOS automatically - no microSD step needed.

Install (command line)

./install.sh                                   # one-time: build the .venv + install Python deps
./diskos-installer doctor                      # check host + bundled tools + device
./diskos-installer install --firmware SNOWSKY_DISC_update_*.zip --variant public
./diskos-installer restore-stock               # deactivate diskOS -> reflash your saved stock rootfs
./diskos-installer remove                      # delete the installer's saved files from this computer

Requirements

  • Your device's official FiiO firmware as a .zip (we never ship FiiO's rootfs - you supply it; download it from FiiO's firmware page for the Snowsky Disc). The installer decrypts and extracts the stock rootfs from it locally.
  • A USB cable and ~60-90 minutes for the flash.
  • Python 3.8+ and the two pip dependencies (installed into a local .venv by ./install.sh).
  • USB access to the mask-ROM device (a108:eaef). Don't run the installer as root - it keeps your saved recovery image and state under your home directory, and sudo would misplace them. Instead install the bundled udev rule once so your normal user has access:
    sudo cp udev/70-diskos-maskrom.rules /etc/udev/rules.d/
    sudo udevadm control --reload-rules && sudo udevadm trigger
    
    (The whole build+save+flash runs as one user process; there is no separate "flash step" to elevate.)
  • Linux (x86_64): this is the released platform - the tarball ships the native flash tools prebuilt in vendor/linux-x86_64/.
  • macOS (Apple Silicon + Intel): no release artifact yet; it's a build-from-source path - clone the repo and run build/build-macos.sh first (libusb via brew install libusb). See For developers. Treat macOS as unverified until built and flashed.

Remove diskOS / restore stock

restore-stock reflashes the stock rootfs the installer built and saved during install - a checksum-verified reconstruction of your firmware's rootfs (not a dump of the device's original partition). This deactivates diskOS; inert diskOS files under /usr/data remain until you delete them - it is not a factory wipe. You can also switch back temporarily: Settings → System → Default UI → Stock, or hold Vol-Up at power-on to boot the other UI once.

If a flash fails or is interrupted, the main rootfs may be partially written and the device may not boot normally. In the tested cases you can return to mask-ROM (power off, hold Vol-Down, replug) and re-flash or restore your saved stock image. Mask-ROM is normally reachable because it lives in on-chip ROM and is entered by a button combo before any flashed code runs, but recovery is not guaranteed on untested hardware or every failure mode.

What's proven vs. beta (honest)

  • ✅ Flash mechanism + image build - proven; it's how diskOS was flashed to real hardware, including the bad-block-aware writer skipping factory bad blocks and verifying every block.
  • ✅ Firmware extraction - reproduces the stock rootfs byte-for-byte from FiiO's zip.
  • ✅ Linux - the app builds and flashes end-to-end.
  • ⚠️ macOS - code is macOS-aware and the build recipe is provided, but the macOS artifact has not yet been produced/tested on Apple hardware. Treat as unverified until built + flashed.
  • ⚠️ Each stock firmware version should be flash-tested before it's declared supported (V2.09 and V2.28 so far).

Known issues (this beta)

diskOS is an early beta. What we know about:

  • Firmware coverage. Only V2.09 and V2.28 are flash-tested. Newer stock versions use different internal command codes and are refused by default (override at your own risk).
  • ~90-minute flash. A full install writes and verifies the whole root filesystem over USB, so it takes roughly 60-90 minutes. Don't disconnect or let the host sleep. (Most of that time is a conservative fixed wait; a faster writer is planned.)
  • No on-device updates. Updating diskOS means re-flashing with the installer; there is no over-the-air path yet.
  • microSD at cold boot. The card is auto-mounted a few seconds into boot. If your library is empty right after a cold boot, reinsert the card once and it should mount.
  • Output/work modes. USB-DAC, Bluetooth-receiver, and USB-storage modes are wired but lightly tested; local playback is the well-worn path.
  • USB-serial debug is unreliable. The optional serial shell can be finicky (a USB CDC-ACM flow-control quirk); prefer Debug Mode's SSH over WiFi for remote access (see below).
  • Last.fm scrobbling is unverified beta. The building blocks (HTTPS transport, request signing, parsing) are tested, but the full connect-and-scrobble round-trip against a live Last.fm account has not yet been run end-to-end. It is off by default and needs your own Last.fm API key; treat it as experimental. Credential setup transfers your API key over your local network in plain HTTP - see docs/PRIVACY.md.

Found something else? Open an issue with your device's firmware version and any on-screen error code.

Debug Mode (optional remote access)

For development or troubleshooting, diskOS can expose a remote shell on demand - it is OFF by default. Open Settings → System → Debug Mode → Enable Debug. The screen then shows:

  • an SSH command (ssh root@<device-ip>) reachable over WiFi, and
  • a fresh random password, generated per-enable and shown on that screen.

⚠️ This grants root access to the device over your network while it is on. Only enable it on a network you trust, and turn it off when you are done. The password is random and rotates each time you enable it; the device's stock password is never used or exposed.

Where the password lives: while Debug Mode is on, the current password is also written in plaintext to /usr/data/sshd/current_pw (mode 0600, root-only) so the screen can show it again after a UI restart. Disabling Debug Mode deletes it. A reboot while it is still on can leave a stale copy - harmless, because a reboot drops the SSH overlay (so that password no longer works until you re-enable), but you can delete the file manually if you want it gone. It is only as protected as physical/root access to the device's storage.

SSH server: Debug Mode uses Dropbear 2022.83, which predates the CVE-2023-48795 "Terrapin" Strict-KEX mitigation (an update is planned). Because Debug Mode is opt-in and short-lived, exposure is limited, but keep it off on untrusted networks. Serial (USB) is available only on dev builds and is unreliable (see the known issue above); public builds expose no serial shell.

How it works (in brief)

diskOS runs by a small hook in the stock fiio_init.sh that launches our UI (mq_ui) instead of the stock one. Because the rootfs is a read-only squashfs, enabling that hook means rewriting the rootfs partition (mtd2) - hence the flash. The UI itself is embedded in the image and installed to the writable /usr/data on first boot, verified against a baked SHA-256 manifest; if it does not match, the stock UI runs instead (fail-closed). You cannot install from the device's own "System updates" menu - that path checks a signature we can't forge, so mask-ROM USB is the only way in. See docs/HARDWARE.md for the partition map and the rest of the hardware.

For developers

The installer already runs from source - ./install.sh then ./diskos-installer (see First-time setup). The release tarball ships the native flash tools prebuilt in vendor/<os>-<arch>/; a fresh git clone does not include them (they are large binaries kept out of git), so build them once:

# Build the native flash tools into vendor/<os>-<arch>/ :
bash build/build-usbboot-static.sh     # static usbboot   (Linux)
bash build/build-squashfs-static.sh    # static mksquashfs/unsquashfs (Linux)
bash build/build-macos.sh              # usbboot + libusb (run on a Mac)

Optionally, you can package everything into a single self-contained binary (this is not how the release ships, and it bundles ~90 system libraries - see licenses/THIRD_PARTY_BUNDLED.md before redistributing one):

bash build/build.sh                    # -> build/dist/diskos-installer (optional onefile)

See build/README-vendor.md for how the native tools are produced and the portability requirements. Working on diskOS with an AI agent? Start with agents/AGENTS.md.

Error codes (for bug reports)

If the installer stops with an error, it prints a short code like [E301]. Quote that code when reporting an issue - it tells us exactly where it stopped. In the tested cases the device stays reachable in mask-ROM and your saved stock image can be re-flashed, but recovery is not guaranteed on untested hardware or every failure mode.

Code Meaning
E1xx Environment / preflight - nothing was written to the device
E101 Unsupported host OS/arch (Linux/macOS only)
E102 A bundled component is missing - re-download the installer
E103 A bundled tool can't run (its directory is mounted noexec, or a missing library)
E110 No device in mask-ROM mode (power off, hold Vol-Down, plug USB)
E111 More than one device in mask-ROM mode - unplug the others
E112 Can't confirm exactly one device (USB permissions / no libusb)
E120 / E121 / E122 Image not found / wrong size / not a squashfs
E140 / E141 No firmware .zip provided / no saved stock image to restore
E142 DISKOS_INSTALLER_HOME points at a non-empty, non-state directory
E2xx Firmware extract & image build
E201 / E202 Input isn't a zip / unsafe zip (path escape or bomb)
E210 FiiO OTA manifest missing, ambiguous, or unsafe
E211 AES-decrypt failed (wrong key or not a FiiO OTA)
E212 / E213 Rootfs chunks missing/duplicate / assembled image invalid
E220 / E221 Not a Snowsky Disc rootfs / untested firmware version
E222 / E223 diskOS UI binary invalid / boot-hook anchor problem
E224 Stock rootfs doesn't match the known-good pinned hash (modified/corrupt firmware)
E230 / E231 / E232 squashfs pack/unpack failed / image too large / output failed validation
E233 A file in the stock rootfs resolves outside it via a symlink (crafted/corrupt firmware)
E240 Stock image larger than the partition (refusing to truncate)
E250 Invalid variant (must be public or dev)
E3xx Flashing (host side)
E301 No result read back - flash outcome UNKNOWN (assume failed, re-flash)
E302 Short/truncated result readback - flash outcome UNKNOWN (assume failed, re-flash)
E303 Flash timed out - the device stopped responding (likely reset mid-flash)
E310 Flash verify failed - see the device code below
F1xx Device writer aborted and reported a coded reason. It fails closed rather than committing a bad block mapping, but blocks erased/written before the abort may already be modified - the rootfs can be partially written. Return to mask-ROM and re-flash or restore stock.
F101…F106 init/ECC, out-of-space, block-write fail, too many bad blocks, ECC re-enable, bad-block marker

Exit codes: 0 success, 1 error, 2 usage/preflight refusal (unsupported host, missing --firmware, bad arguments, or Tk unavailable for the GUI), 3 you cancelled at a prompt, 130 interrupted.

Support

diskOS is a solo, open-source hobby project. If it's useful to you and you'd like to help keep the work going, you can leave a tip:

Ko-fi

ko-fi.com/b0hemia - entirely optional, and hugely appreciated.

License

  • Original diskOS installer files in this repo (the Python installer, build scripts, docs) are MIT - see LICENSE. SPDX: MIT.
  • Third-party components retain their own licenses and ship with corresponding source: the native flash tools usbboot and squashfs-tools (GPL-2.0), the libraries they statically link (liblzo2 GPL-2.0, libusb-1.0 LGPL-2.1, zlib/liblzma), and the stage-1 SPL (GPL-2.0). The corresponding source for all of these ships in corresponding-source/ (SPL also in spl-src/ + SPL_SOURCE.md); see NOTICE.md for the full mapping and the relink path for the LGPL libusb.
  • The diskOS UI (payload/mq_ui) currently ships as a binary-only component. It is not covered by the MIT license above and its source is not yet published; treat it as a redistributable binary whose provenance, version, and third-party content are still being documented. If/when the UI source is published this note will be updated.

Do not redistribute the diskos_*.bin images the installer builds - they contain FiiO's rootfs. The installer produces them locally from your firmware; that is fine for your own use, but sharing them would redistribute FiiO's software. Share the installer, not the built image.