The build turns the vendor's Tina SDK into one SD1 image per board. Each result lands in
builds/<stamp>-<board>/, beside a BUILD-INFO.txt that records what went into it: the
SDK checksum, toolchain, kernel object, U-Boot and overlay hashes, and the C library.
A glibc 2.38 / libstdc++ 13 userland (Arm GNU Toolchain 13.3; why newer than Moss's:
docs/toolchain.md) on the vendor's 4.9.191 kernel, with ncurses 6, libmad, bluez with
bluez-alsa 4.0.0, and a BusyBox that has unzip, losetup and stat. The kernel has loop
devices, uinput and joydev built in, squashfs that reads XZ, LZO, LZ4, gzip and zstd
images, and drivers for both radios (XR829, Realtek 8189es). The PowerVR GE8300 blobs are
included, and the TrimUI SDL2 libraries the launcher expects are under /usr/magicx/lib.
A Linux host with Docker. The SDK only builds in an Ubuntu 18.04 userland, and the
scripts build that container for you. Host 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 pictures the scripts draw (boot logo,
bootloader and charge screens).
About 45 GB of disk for the inputs and one board's build. The unpacked SDK takes ~18 GB,
~30 GB once built; inputs/ holds ~13 GB of archives; every extra image lane adds its own
build tree. OAKMOSS_WORK moves the large trees to another disk (scripts/lib.sh).
The inputs. Part of what the build needs is proprietary and never stored in this
repository. scripts/fetch-inputs.sh downloads and verifies everything that is public
(including the XU20's and Zero 40's stock firmwares, which are public releases), then
reports what is still missing and where it goes.
scripts/fetch-inputs.sh # public inputs, and a check for the private ones
scripts/prepare-toolchain.sh # Arm GNU 13.3 plus the Tina sysroot and wrapper shims
scripts/build-openixcard.sh # OpenixCard from source (Allwinner image -> raw image)
scripts/unpack-sdk.sh --remove-zip # stream the SDK out of its zip and verify it
scripts/apply-sdk-mods.sh # every SDK change (docs/sdk-mods.md); safe to rerun
scripts/build.sh uboot # our U-Boot with the Zero 40 and XU20 panels (after every SDK unpack)
scripts/make-boot-resource.py --logo zero28 overlay/bootlogo.bmp # optional: the oakMOSS boot logo
scripts/build.sh all zero28 zero40 xu20 # the userland (~20-40 min on 8 cores), then one image per boardscripts/build.sh image <board> rebuilds one board's image without the userland step. The
image to flash is builds/<stamp>-<board>/oakmoss-<board>-<stamp>-sd1.img; COMPRESS=1 also
writes an .img.xz. build.sh image refuses a U-Boot binary that lacks the panels, or an
Android one: after a fresh SDK unpack, run build.sh uboot first.
Only the first row is what a board should run today. The others are how the boards were brought up, and stay in the repository for reference.
| image | boards | what it is |
|---|---|---|
oakmoss-<board>-<stamp>-sd1.img |
Zero 28, Zero 40, XU20 V32 | The one to flash. The whole chain is ours or the SDK's: SDK boot0 and ATF/SCP, our U-Boot with the boards' panels (sdk-patches/tree/103), our device tree, kernel and root filesystem. It is the only chain that reaches charge mode. |
oakmoss-<board>-own-<stamp>-sd1.img |
XU20 V32, Zero 40 | The board's stock boot0 and U-Boot in front of our kernel, device tree and root filesystem (scripts/make-own-kernel-stock.sh). Used until 2026-09-28; a board plugged in while off stays off. |
oakmoss-zero40-stock-<stamp>-sd1.img |
Zero 40 | The Zero 40's stock firmware chain and Android kernel with our root filesystem (scripts/make-hybrid-zero40-stock.sh). |
oakmoss-xu20-hybrid-<stamp>-sd1.img |
XU20 V32 | The XU20's stock boot chain and Android kernel with the Zero 40 root filesystem (scripts/make-hybrid-xu20.sh). Never booted on a unit. |
oakmoss-zero40-hybrid-<stamp>-sd1.img |
Zero 40 | main-zero40's boot chain with our root filesystem and MagicX's kernel modules (scripts/make-hybrid-zero40.sh). |
The older lanes, from a finished build:
scripts/make-hybrid-zero40.sh builds/<stamp>-zero40 # main-zero40 chain
scripts/build-awutils.sh # XU20: the unpacker for its PhoenixSuit image
scripts/unpack-xu20-stock.sh # XU20: image items and vendor modules
scripts/make-hybrid-xu20.sh builds/<stamp>-zero40 # XU20 on its stock chain, Android kernel
scripts/unpack-zero40-stock.sh # Zero 40: items and modules from MagicX's adb.img
scripts/make-hybrid-zero40-stock.sh builds/<stamp>-zero40 # Zero 40 on its stock chain, Android kernel
scripts/make-boot-resource.py inputs/xu20-stock/user.img.dump/RFSFAT16_BOOT-RESOURCE_FE \
builds/bootres-xu20.fex xu20 # bootloader screens 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 # stock boot0/U-Boot + our kernel(The Zero 40 takes the same make-own-kernel-stock.sh route from inputs/zero40-stock/adb.img.dump.
Without DIAG=0 those images carry the diagnostic hand-off; the script's header lists its options.)
scripts/build.sh contresumes a build without wiping it.- After changing a package Makefile, remove
sdk/lichee/out/a133-aw3/compile_dir/target/<pkg>*. scripts/build.sh image <board>repacks without a full rebuild.- Kernel options go in
config-4.9throughsdk-patches/tree/020. The build then appends the top config'sCONFIG_KERNEL_*lines, and the later line wins: that is how the rootfs compressor choice once switched squashfs decompressors off (134,docs/sdk-mods.md, Round 17). Check what the kernel really got insdk/lichee/lichee/linux-4.9/.config, or in the image'sSystem.map;build.sh imagereconfigures the kernel only when that merged configuration changes.
scripts/make-diag-image.shturns any image into a colour-staged diagnostic boot that leaves a full hardware snapshot on the card (docs/hardware-notes.md).EXPERIMENTS="zero40-radio"adds a bench experiment fromdiag/experiments/; a normal diagnostic image carries none.- Debug kernels (
sdk-patches/debug/) are built withscripts/kdebug.sh build <board>, which checks that the debug patch matches the tree first. Before a file's first debug edit runscripts/kdebug.sh stash <file>; after editing,scripts/kdebug.sh regenrebuilds the patch.scripts/kmark-env.py,scripts/kmark-pm-decode.pyandscripts/kmark-ring.pyread what such a kernel leaves behind;docs/hardware-notes.md("Sleep on the SDK chain") shows them in use.
The scripts never force a patch onto the SDK tree: when a patch and the tree disagree they stop and change nothing.
The scripts that write into an image (replace-rootfs.sh, the XU20 builder) check that
everything outside the partition they wrote still matches the source byte for byte. They
flush with a bounded, reported sync of the one file, because a wedged mount on the build
host once left two dozen sync processes blocked and a build hung with nothing to write.
| path | what |
|---|---|
scripts/ |
The build, in order: fetch-inputs, prepare-toolchain, build-openixcard, unpack-sdk, apply-sdk-mods, build.sh (uboot, all, image, cont, ...). Pictures: make-boot-resource.py (bootloader screens, boot logo), make-charge-screens.py (charge and low-battery screens). Board trees: make-board-dts.py. Debug: kdebug.sh, kmark-env.py, kmark-pm-decode.py, kmark-ring.py. Cards: analyze-card-readback.py, verify-card.ps1. Older lanes: make-own-kernel-stock.sh, make-hybrid-stock.sh and its wrappers, make-hybrid-zero40.sh, adapt-zero40-rootfs.sh, unpack-*-stock.sh, build-awutils.sh. Helpers: run-in-sdk.sh, replace-rootfs.sh, fdt-enable-nodes.py, make-diag-image.sh, and lib.sh (paths, pinned checksums). |
sdk-patches/ |
Every change to the SDK tree: tree/ (unified diffs), package-patches/, ncurses-6.2/, gcc-7.5.0-patches/, toolchain/, sunxi_encrypt/ (ENCRYPT_OBJ=stub), and debug/ (applied only for debug kernels). Each is recorded in docs/sdk-mods.md. |
boards/<board>/ |
Per board: board.conf (device marker, kernel object), board.dts, sys_config.fex (charge mode), sys_partition.fex, boot-resource/ (U-Boot's charge and low-battery pictures, and the Zero 40's and XU20's boot logo) and overlay/ (the charge screen frames), layered over the shared overlay/. |
overlay/ |
Root filesystem additions: etc/rc.local (audio defaults, the hand-off), etc/init.d/chargeframe (the first charge frame), usr/magicx/bin/runmagicx.sh and charge-screen.sh, the TrimUI SDL2 libraries under usr/magicx/lib/, and small fixes (wpa_supplicant no-op, empty modules.d/net-xr829). A bootlogo.bmp here (gitignored) replaces the SDK's logo. |
configs/ |
Tina configs: ext-armgnu13.config (the build), Moss's phase1/phase2 and the from-source fallback phase2-gcc750. |
tools-patches/ |
Patches to tools built from source (awutils, for the XU20 image header). |
docker/ |
The Ubuntu 18.04 build container. |
diag/ |
The diagnostic hand-off, fbfill.c, the input-bitmap decoder, and opt-in experiments/. |
assets/ |
spruceOS's tree mark, for the boot logo. |
docs/ |
This guide, using.md, status.md, sdk-mods.md (ledger of SDK changes), hardware-notes.md, toolchain.md, DEPENDENCIES.md. |
LICENSES/ |
License texts for third-party code carried here. |
inputs/, sdk/, toolchains/, tools/, builds/ |
Working trees, not tracked. |