Skip to content

Repository files navigation

oakMOSS

Base-OS ("SD1") image for the MagicX Mini Zero 28, MagicX Zero 40 and XU RETRO / MagicX XU20 V32 (Allwinner A133P). A pared-down Tina Linux whose only job is to boot, mount the user card in the second slot at /mnt/SDCARD and hand off to the launcher on it: spruceOS's .tmp_update/updater, or magicx/init.sh if a card carries one. It follows the shape of Shaun Inman's Moss-zero28 and main-zero40; see ATTRIBUTION.md.

What comes out, per board, as raw GPT images (686-796 MB, mostly empty) that are written to the internal microSD (see "Using the image"):

image board what it is
oakmoss-zero28-<stamp>-sd1.img Zero 28 our full chain: boot0, U-Boot, DTB, kernel, rootfs
oakmoss-zero40-<stamp>-sd1.img Zero 40 same chain with the Zero 40 kernel object; white screen on the real board (the SDK drop lacks its panel and touch drivers)
oakmoss-zero40-hybrid-<stamp>-sd1.img Zero 40 the earlier way in: main-zero40's boot chain (panel, touch, kernel) with our rootfs in its rootfs partition, carrying MagicX's own kernel modules (scripts/adapt-zero40-rootfs.sh)
oakmoss-<board>-own-<stamp>-sd1.img XU20 V32, Zero 40 the one to flash on these two: the board's stock boot0 and U-Boot with OUR kernel, device tree and rootfs (scripts/make-own-kernel-stock.sh); the panels come from sdk-patches/tree/070-*, the touch drivers from 080-*, the trees from boards/<board>/board.dts
oakmoss-zero40-stock-<stamp>-sd1.img Zero 40 new lane (2026-09-16): the Zero 40's own stock firmware chain (MagicX release Zero40_V1 adb.img: boot0, U-Boot, DTB, Android kernel 4.9.170) with the Zero 40 rootfs as ext4 and the vendor's modules, the same recipe as the XU20 image; built because no round-8 rootfs ever came up on the main-zero40 chain
oakmoss-xu20-hybrid-<stamp>-sd1.img XU20 V32 the stock firmware's own boot chain and Android kernel (its panel and touch driver are in neither our SDK nor main-zero40) with the Zero 40 rootfs as ext4; both extra SD controllers enabled so the second card is visible. Not yet booted on a unit.

Rootfs: glibc 2.38 / libstdc++ 13 userland (Arm GNU Toolchain 13.3) on the vendor 4.9.191 kernel, ncurses 6, libmad, bluez, busybox with unzip/losetup/stat, loop + uinput + joydev in the kernel, XR829 and Realtek 8189es radio support, PowerVR GE8300 blobs, and the TrimUI SDL2 blobs under /usr/magicx/lib that the launcher expects. Why the userland is newer than Moss's: see docs/toolchain.md.

Inputs you have to bring

Some of what the build needs is proprietary and is never in this repository. scripts/fetch-inputs.sh downloads and checksum-verifies everything that is publicly available, then tells you what is still missing and where it goes. The XU20's and the Zero 40's stock firmwares are public GitHub releases and are fetched automatically.

Build

Host: Linux with Docker (the SDK needs an Ubuntu 18.04 userland; the container is built for you) and these packages: git curl unzip xz-utils squashfs-tools util-linux cmake build-essential libconfuse-dev pkg-config automake autoconf, plus python3-pil fonts-dejavu-core for the boot logo and bootloader screens (scripts/make-boot-resource.py).

Disk: budget about 45 GB for the inputs plus one board's build. The unpacked SDK alone is ~18 GB clean and ~30 GB once built, inputs/ holds ~13 GB of SDK and stock-firmware archives, and every extra image lane (hybrid, stock, own, XU20) adds its own builds/<stamp>-<board> tree on top.

scripts/fetch-inputs.sh            # public inputs; checks the private ones
scripts/prepare-toolchain.sh       # Arm GNU 13.3 + the Tina sysroot/wrapper shims
scripts/build-openixcard.sh        # OpenixCard from source (Allwinner image -> raw image)
scripts/unpack-sdk.sh --remove-zip # streams the SDK out of the zip, verifies its md5
scripts/apply-sdk-mods.sh          # every SDK change (docs/sdk-mods.md), idempotent
scripts/make-boot-resource.py --logo zero28 overlay/bootlogo.bmp   # optional: the oakMOSS boot logo (else the SDK's)
scripts/build.sh all               # userland build (~20-40 min on 8 cores) + both images
scripts/make-hybrid-zero40.sh builds/<stamp>-zero40     # the image a Zero 40 boots
scripts/build-awutils.sh           # XU20 only: the unpacker for its PhoenixSuit image (patched for its header)
scripts/unpack-xu20-stock.sh       # XU20 only: image items + vendor modules out of the stock firmware
scripts/make-hybrid-xu20.sh builds/<stamp>-zero40       # the XU20 image (from the Zero 40 rootfs)
scripts/unpack-zero40-stock.sh     # Zero 40 stock lane: items + vendor modules out of MagicX's adb.img
scripts/make-hybrid-zero40-stock.sh builds/<stamp>-zero40   # the Zero 40 image on its own stock chain (Android kernel; no GL)
scripts/build.sh image xu20                                  # our kernel + rootfs for the XU20 (boards/xu20/board.dts, panel in 070-*)
scripts/make-boot-resource.py inputs/xu20-stock/user.img.dump/RFSFAT16_BOOT-RESOURCE_FE \
    builds/bootres-xu20.fex xu20                               # bootloader screens + boot logo sized for the panel
DIAG=0 EARLY_PROBE=0 CONSOLE=tty0 BOOTRES=builds/bootres-xu20.fex \
    scripts/make-own-kernel-stock.sh xu20 builds/<stamp>-xu20  # the card: stock boot0/U-Boot + our kernel, tree and rootfs
#   (zero40 the same, from inputs/zero40-stock/adb.img.dump; without DIAG=0 the card carries the
#   diagnostic hand-off, and the script's header lists the debug options)
#   (both wrappers run scripts/make-hybrid-stock.sh <board>: one recipe, two boards)

Outputs land in builds/<stamp>-<board>/ with a BUILD-INFO.txt (SDK md5, toolchain, kernel object, overlay hash, libc). COMPRESS=1 also writes .img.xz. Everything is overridable through the environment (OAKMOSS_WORK moves the big trees to another disk; see scripts/lib.sh).

Iterating: scripts/build.sh cont resumes without wiping; after changing a package Makefile remove sdk/lichee/out/a133-aw3/compile_dir/target/<pkg>*; scripts/build.sh image <board> re-packs without a full rebuild. scripts/make-diag-image.sh turns any image into a colour-staged diagnostic boot with a full hardware snapshot on the card (docs/hardware-notes.md). EXPERIMENTS="zero40-radio" adds a bench experiment from diag/experiments/ to that image; a normal diagnostic image carries none.

The writers (replace-rootfs.sh and the XU20 builder) verify their output the hard way: everything outside the partition they wrote must match the source image byte for byte, and the flush is bounded and reported rather than a bare sync (one wedged mount on the build host once left two dozen sync processes blocked behind it, and a build hung with nothing left to write).

Using the image

This replaces the firmware your device boots from. Writing the wrong image to the wrong board, or interrupting the write, leaves a device that will not start. Image the stock SD1 card first and keep that backup: for these boards it is the only way back. Write the image raw (dd, Rufus in DD mode, Etcher, Raspberry Pi Imager) to the whole card, then eject. Do not open a written card in a partition tool - a table "repair" zeroes boot0 and the card stops booting. On Windows, the system itself rewrites the table of an XU20 or Zero 40 card whenever it detects the card again (docs/hardware-notes.md): write it with Raspberry Pi Imager, verification on, take it to the device when Imager ejects it, and do not plug it back in before it has booted. scripts/verify-card.ps1 compares a written card with its image without Windows storage management.

  1. Write the image to the internal microSD (SD1, the whole card). The system card goes back in the slot the board's own system card came from.
  2. Put a launcher card in the second slot: a FAT32 card carrying spruceOS with MagicX support (its .tmp_update/updater reads /usr/magicx/device to tell zero28 from zero40), or any card with magicx/init.sh. exFAT is not supported: kernel 4.9 here has no exFAT driver, so the user card must be FAT32.
  3. Boot. With no card, or if the launcher exits, the device powers off (overlay/usr/magicx/bin/runmagicx.sh).

Layout

path what
scripts/ the build, in order: fetch-inputs, prepare-toolchain, build-openixcard, unpack-sdk, apply-sdk-mods, build, make-hybrid-zero40 (which runs adapt-zero40-rootfs), make-diag-image; the stock-chain lane: unpack-xu20-stock / unpack-zero40-stock, then make-hybrid-stock <board> (wrappers make-hybrid-xu20, make-hybrid-zero40-stock); for the XU20 build-awutils, unpack-xu20-stock, make-hybrid-xu20; run-in-sdk.sh runs a command in the container; replace-rootfs.sh writes a squashfs into an image's rootfs partition and proves the rest untouched; fdt-enable-nodes.py enables device-tree nodes in place; lib.sh holds paths, pinned checksums and flush_file; the own-kernel lane: make-own-kernel-stock.sh, make-board-dts.py (board trees from the stock ones), make-boot-resource.py (bootloader screens and the boot logo); analyze-card-readback.py reads the boot record off a card read back raw; verify-card.ps1 checks a written card on Windows
tools-patches/ patches to tools built from source: awutils' image-format check for the XU20 firmware's header
configs/ Tina configs: ext-armgnu13.config (the build), phase1.config / phase2.config (Moss-zero28's originals, plus libmad) and phase2-gcc750.config (the from-source fallback toolchain)
sdk-patches/ every change to the SDK tree: tree/ (unified diffs applied with patch -p1), package-patches/ (dropped into package/<pkg>/patches/), ncurses-6.2/ (OpenWrt 21.02 package port), gcc-7.5.0-patches/ (OpenWrt 19.07 set), toolchain/ (compiler wrapper), sunxi_encrypt/ (ENCRYPT_OBJ=stub), debug/ (kernel instrumentation build.sh applies only when asked, KDEBUG_MARK=1)
assets/ spruce-tree.png, spruceOS's tree mark, which make-boot-resource.py turns into the boot logo
LICENSES/ license texts for the third-party code carried here (GPL-2.0)
overlay/ rootfs additions: etc/rc.local (audio defaults + the hand-off), etc/banner, etc/modules.d/net-xr829 (empty: no xradio autoload), etc/init.d/wpa_supplicant (no-op: the launcher owns the radio, the SDK's service ran a second supplicant on wlan0), usr/magicx/bin/runmagicx.sh, usr/magicx/lib/ (TrimUI SDL2 blobs). A bootlogo.bmp here (gitignored) replaces the SDK's logo; scripts/make-boot-resource.py --logo zero28 draws the oakMOSS one
TODO.md wishlist for the kernel (config, per-board drivers and DTB) and the rootfs overlay, with what is done and what still needs identifying
boards/<board>/board.conf device marker and kernel-object source per board; boards/<board>/overlay/ (optional) is layered on top of overlay/
docker/ the Ubuntu 18.04 build container
diag/ diagnostic hand-off, fbfill.c, the input-bitmap decoder, and experiments/: opt-in bench scripts (zero40-radio) the hand-off runs only when an image was built with them
docs/ sdk-mods.md (ledger of SDK changes, with reasons), toolchain.md, hardware-notes.md (what the boards turned out to be), DEPENDENCIES.md (what spruce and PortMaster need from the base)
inputs/, sdk/, toolchains/, tools/, builds/ untracked working trees (.gitignore)

Status

Bench-tested on a Zero 28, a Zero 40 and an XU20 V32 with spruceOS on the user card (September 2026); not a release. The Zero 28 runs the SDK chain. The Zero 40 and the XU20 run our kernel behind their stock boot0 and U-Boot (scripts/make-own-kernel-stock.sh): launcher, pad, touch, audio and WiFi exercised on both. Their panels (sdk-patches/tree/070-*) and touch controllers (080-*) are in our kernel, so the hybrid images that borrowed MagicX's Android kernel are no longer the way in. Touch is a module that spruce loads once the board has settled: built in, its probe stalled boots at the logo (docs/hardware-notes.md, "Touch drivers stalled the boot"). Known gaps: the newer-board-revision fixes in main-zero40 v20260202-1 are not in our inputs, suspend-to-RAM never resumes (seen on the XU20; spruce uses a pseudo-sleep on these boards), kernel 4.9 has no exFAT (large SD2 cards need FAT32), busybox 1.27 has no bc, and the XR829 crystal variant is assumed 26 MHz. The Zero 28's and XU20's radio is a Realtek 8189es, the Zero 40's an XR829, both driven by in-kernel drivers.

Every board's MagicX kernel carries a sunxi_encrypt object that reboots the kernel unless the board's I2C security chip answers to its key (docs/hardware-notes.md); the XU20 boots with the Zero 40's object (boards/xu20/board.conf). A USB-TTL UART adapter on PB9 (TX) / PB10 (RX) at 115200 8N1 and the diag lane's colour ladder are the diagnostics that have told us anything on these boards. Treat every image here marked untested as exactly that.

The spruceOS side (platform files, PyUI device classes, the card builder) lives in spruceOS, not here.

oakMOSS is an independent community project. It is not affiliated with, endorsed by, or supported by MagicX, XU RETRO, TrimUI, Allwinner or Imagination Technologies, and it is not Moss or Dedicated OS. Device, product and company names are used only to say which hardware and which sources are involved. Flashing a device is at your own risk; see LICENSE.

License: LICENSE for oakMOSS's own files; third-party terms in ATTRIBUTION.md.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages