Archives: Boox Knowledge Base

  • Device Hub — Note Air 1

    Device Hub — Note Air 1

    Canonical hardware reference for the Onyx Boox Note Air 1. All facts here are confirmed from physical device inspection (Phase 01) and EDL partition dumps. Use this as the ground truth when making porting decisions.


    Hardware Identity

    FieldValue
    Marketing nameOnyx Boox Note Air
    Model (ro.product)NoteAir
    SoCQualcomm SDM636 (Snapdragon 636) — “sdm660” platform
    CPUKryo 260 (4×A73 + 4×A53), arm64-v8a
    GPUAdreno 509
    RAM3 GB LPDDR4
    StorageeMMC (not UFS)
    Display10.3″ E-ink, 1872×1404, driver: onyx_epdc_fb
    TouchWacom EMR (i2c@c1b6000/wacom@09) + capacitive overlay
    Android version10 (SDK 29)
    Kernel4.4.x (Onyx binary, no source published)
    VNDK29
    Boot image headerv0, page size 2048
    TrebleEnabled (ro.treble.enabled=true, separate vendor partition)
    Partition schemeSingle-slot, static (GPT)
    EncryptionFDE (full-disk encryption, auto-encrypt, no password)
    Userdata FSext4

    Partition Table (from EDL printgpt)

    PartitionSizeNotes
    xbl3.5 MBExtensible bootloader (XBL) — chip-specific, do not replace
    abl256 KBAndroid bootloader — custom ABL v2.4 currently flashed
    boot64 MBKernel + ramdisk (header v0)
    recovery64 MBTWRP v3.7 currently flashed
    system3.5 GBAOSP + Onyx apps (ext4)
    vendor1 GBQualcomm sdm660 HALs (ext4)
    userdata~50 GBUser data (ext4, FDE)
    persist32 MBCalibration data — never wipe
    misc4 MBBCB (bootloader control block)
    modem100 MBBaseband firmware
    cache256 MBAndroid cache

    Key HAL Blobs (vendor partition)

    BlobPurpose
    hwcomposer.sdm660.soDisplay composer — calls onyx_epdc_set_mode() kernel symbol
    gralloc.sdm660.soGraphics buffer allocator
    android.hardware.graphics.composer@2.1-serviceHWC service
    libdisplayconfig.soDisplay config library

    EPDC Driver Notes

    The e-ink display controller (EPDC) driver is compiled into the kernel binary, not a loadable module. Device node: /dev/ebc. Sysfs: /sys/devices/sepdc/. Compatible string: onyx,sepdc.

    Two update paths exist: (1) /dev/ebc ioctl — used by TWRP and recovery, subject to waveform transform; (2) onyx_epdc_set_mode() kernel symbol — called directly by HWC, bypasses transform. See Research → A2 Waveform Mode for full analysis.


    Fastboot Behaviour

    CommandResult
    fastboot flash boot boot.imgWorks — verified
    fastboot flash recovery recovery.imgWorks — verified
    fastboot flash abl abl.elfWorks — verified (use qtestsign -v 5)
    fastboot oem unlockNot needed — bootloader already unlocked
    fastboot getvar allWorks — returns device info

    Backups

    Full EDL partition dumps stored locally in firmware/backups/ (excluded from git via .gitignore). Key partitions backed up: xbl, abl, boot, recovery, system, vendor, persist, misc, modem. Always verify MD5 checksums against the backup manifest before flashing.

  • Session Log Hub

    Chronological session diary — one entry per working session. Sessions span 2026-03-16 through 2026-03-30. Source: docs/diary/.

    These are raw session logs — “things that were tried and what happened”. Confirmed facts live in the Project State hub and device/phase entries. Use diary entries to cross-check assumptions, understand dead ends, and trace the history of specific discoveries.

    📅 Session Index

    Session #DateKey Activity
    12026-03-16Tooling setup, device exploration, EDL printgpt, pristine backup
    22026-03-16bRooting with Magisk v30.7 via EDL, vendor HAL inspection
    3–82026-03-16c–g, 2026-03-17TWRP device tree, build environment, first flash attempts, error 0x50 root cause
    92026-03-23aGhidra ABL analysis — signing, fastboot hang, battery check
    10–122026-03-23b–c, 2026-03-24aABL source analysis, build toolchain, custom ABL v1.0–v1.2 (FDE loops)
    132026-03-24a (cont.)VBSendRot FDE fix, ABL v1.3 boots Android
    142026-03-25aEDL/host environment: qcserial blacklist, Sahara HELLO timing
    15–192026-03-26a–cABL v1.3–v1.6, fastboot hang investigation, DisplayFastbootMenu fix
    20–262026-03-27a–hTWRP v1.4–v2.6: display fix found (waveform file), touch rotation fixed
    272026-03-27iABL v2.0 brick, v2.1 recovery; TWRP v3.0–v3.3 touch confirmed working
    28–292026-03-28a–cTWRP crypto analysis, GCC CNOC clock research, ABL v2.2 findings
    302026-03-29aABL v2.3/v2.4: XPU discovery, south tile GPIO, hall sensor WORKING
    31–332026-03-29b–dA2 waveform RE, ebc-probe (crashes in TWRP), kernel binary patch tool
    342026-03-29eStock ABL RE for EDL cable detection, ABL v2.5 oem detect-port
    352026-03-30NA2 firmware acquisition, NA2 device facts, compatibility matrix
  • Research Hub

    Deep technical research produced during the project. Each child entry documents a specific investigation, including root cause analysis, findings, and action plans. Source: docs/research/.

    📚 Research Topics

    TopicStatusKey Finding
    A2 Waveform Mode — Kernel Transform✅ Research complete; fix readyKernel onyx_waveform_mode_transform() at 0x427a8c maps A2→GC16. Fix: NOP the BL at offset 0x41d410. Tool: patch-kernel-waveform-transform.py.
    GCC/TLMM Clock Enable on SDM636/SDM660✅ Research complete; device verification pendingNo dedicated GCC_TLMM_AHB_CLK on SDM660. TLMM sits on CNOC bus. CBCR offsets confirmed from Qualcomm BSP. But XPU blocking (not clocks) was the real issue — see ABL south tile discovery.
    Stock Recovery Display Init (strace)✅ Complete — authoritative sequence documentedFull syscall sequence for e-ink init confirmed. EBC_SEND_UPDATE before FBIOBLANK is the key step. Input device map for all 7 event devices established.

  • Tools Hub

    Reference documentation for every tool used in the project. Child entries cover each tool in detail. See docs/tools/.

    🧰 Toolchain Overview

    ToolLocationWrapper / Notes
    adb / fastbootsystem (android-tools via pacman)v35.0.2 / adb 1.0.41. Use adb --version, not adb version.
    edl.pytools/opt/edl/via tools/edl-run wrapper
    decryptBooxUpdateUpxtools/opt/decryptBooxUpdateUpx/via tools/deboox-run wrapper
    payload-dumper-gotools/opt/payload-dumper-go/run directly
    magiskboottools/opt/magiskbootextracted from Magisk v30.7 APK
    Python venvtools/opt/venv/project-local; do not activate manually
    TWRP build envDocker image twrp-builder:latestUbuntu 20.04 — ALL TWRP builds run in Docker
    Ghidra REDocker image ghidra-re:latestv11.1.2, via tools/ghidra-run
    ABL buildtools/abl-build <version>Docker — builds AND signs with qtestsign -v 5. Never use manually.
    qtestsigntools/opt/qtestsign/qtestsign.pyMust use -v 5 for SDM636. Default -v 3 is silently rejected by XBL.
    udev rules/etc/udev/rules.d/51-android.rulesADB 4ee2, fastboot d00d, EDL 9008, EDL 900e

    ⚠️ Known Gotchas

    • adb version (no dashes) starts the ADB daemon and hangs in non-interactive scripts. Use adb --version.
    • simg2img with no args reads from stdin and hangs. Check presence with command -v simg2img.
    • Python 3.14 on Arch has no pip. Use python3 -m venv to create a self-contained venv.
    • AIK (Android Image Kitchen) master is Windows-only. Use magiskboot for all boot image operations.
    • newgrp plugdev activates the group in the current shell without a full logout.
    • qcserial must be blacklisted (/etc/modprobe.d/no-qcserial.conf) — it auto-loads on 9008 connect and steals the Sahara HELLO.
  • Risk Register

    All known risks, categorised by severity. Review before any destructive operation. Source: docs/risks.md.

    🔴 Critical — Potential Hard Brick or Data Loss

    IDRiskTriggerMitigation
    R01Flash wrong bootloader (XBL/ABL)Copying NA2 xbl.elf/abl.elf to NA1Never flash XBL or ABL from NA2 onto NA1. A UFS bootloader on eMMC hardware = permanent brick.
    R01bFlash custom XBLAny XBL modificationNever touch the xbl partition. ABL failure is recoverable via EDL. XBL failure = hard brick with no recovery.
    R02EDL 900E — no recovery loaderDevice fully unresponsive in 900EEDL 9008 is the only recovery. Keep a working OS. Never flash boot without a verified backup.
    R03Flash during low batteryDevice powers off mid-writeInstaller checks battery ≥ 40%. Do not flash manually on low battery.
    R04Wrong programmer in EDLUsing NA2 UFS programmer on NA1 eMMCNA1 = prog_emmc_ufs_firehose_Sdm636_ddr.elf. NA2 = UFS programmer. Never mix.
    R12ABL crash → no 9008, only battery-drain recoveryABL uses unmapped MMIO, bad pointer, etc.v2.0 incident: Raw MmioRead32(0x03069004) caused data abort → device bricked. Recovery required full battery drain + cold-boot PBL 9008. Never use raw MMIO without GCD memory mapping. Always identify last known-good ABL before flashing a new version.

    🟠 High — Functionality Loss, Requires Recovery

    IDRiskTriggerMitigation
    R05Partition size mismatchNA2 image larger than NA1 partitionAlways check image size vs. partition size from Phase 01 before flashing.
    R06Incorrect fstab block pathsBooting NA2 system with UFS paths on eMMCPhase 05 core task. Never flash un-patched system/vendor from NA2.
    R07Incremental OTA on rooted deviceUpdate via OTA while rootedUse only full firmware images. Block OTA in AFWall+ during development.
    R08Accidental userdata wipeWrong option in recoveryTWRP on e-ink is confusing. Proceed slowly and confirm each action.

    🟡 Medium — Degraded Functionality

    IDRiskTriggerMitigation
    R09Loss of root after firmware flashFull OTA updateExpected behaviour. Re-root using Phase 02 procedure.
    R10Onyx telemetry during testingUnblocked networkInstall AFWall+ immediately after rooting.
    R11GPL compliance issuesPublishing firmware with Onyx kernel binaryDo not redistribute modified boot.img binaries in a way that implies GPL compliance.

    🚨 Emergency Recovery Procedures

    EDL 9008 — Boot Partition Restore

    # 1. Trigger EDL 9008 (software, from Android)
    adb reboot edl
    
    # 2. Restore pristine boot partition
    python edl.py --loader=prog_emmc_ufs_firehose_Sdm636_ddr.elf 
      w boot builds/backup-YYYY-MM-DD-pristine/boot.img
    
    # 3. Reboot
    python edl.py --loader=prog_emmc_ufs_firehose_Sdm636_ddr.elf reset

    FDE RoT Mismatch — No EDL Required

    If the device boots to the FDE password prompt but no password works (RoT changed due to ABL swap):

    1. Enter any wrong password repeatedly at the FDE prompt (~30 times).
    2. At ~7 remaining attempts: warning appears.
    3. After ~30 total: “Encryption unsuccessful” screen → tap factory reset.
    4. Device wipes userdata and re-encrypts under the current ABL’s RoT. Only userdata is lost — system and ABL preserved.
  • Project State — Living Summary

    ⚡ Current Device State

    Last updated: Session 34 — 2026-03-29. ⚠️ Action required at next session start: restore ABL v2.4.

    ItemState
    Physical device✅ Fully operational. Hall sensor boot mode working.
    Android✅ Booting normally (cover open)
    ADB✅ Available
    Fastboot⚠️ Command channel works; bulk transfer broken
    EDL 9008✅ via adb reboot edl or fastboot oem reboot-edl
    Recovery partitionTWRP v3.7 — crypto disabled, A2 dither fix, WAIT_UPDATE added
    BCB (misc)Clear
    ABL partition⚠️ v2.5 — broken oem detect-port. Restore v2.4 at next session start.
    UserdataEncrypted (FDE). Boots normally, no prompt.
    Pristine backup✅ builds/backup-2026-03-16-1532-pristine/ — SHA256SUMS verified
    Rooted backup✅ builds/backup-2026-03-16-1637-rooted/ — SHA256SUMS verified
    Boot mode selection✅ Close cover + power on → 3 s confirm → TWRP

    🔑 Last known-good ABL: v2.4 — builds/abl/abl-v2.4.elf
    🔙 Fallback ABL: v1.6 — builds/abl/abl-v1.6.elf


    🗺️ Phase Status

    #PhaseStatus
    00Tooling setup✅ Complete
    01Device exploration✅ Complete
    02Rooting✅ Complete — Magisk v30.7, su confirmed
    03aCustom UEFI ABL✅ Complete — v2.4: VBSendRot, hall sensor, display fix, OEM cmds
    03bCompile TWRP✅ Stable — display + touch working (v3.3+), v3.7 current
    03b2Boot mode selection✅ Complete — close cover + power on → TWRP
    03cA/B partition migration🔴 Not started
    04Acquire NA2 firmware🔴 Not started
    05Build adaptation🔴 Not started
    06Testing & iteration🔴 Not started
    07User installer🔴 Not started
    08Documentation & web🔴 Not started
    09Treble investigation🔴 Not started
    10apostmarketOS (vendor kernel)🔴 Not started
    10bMainline kernel port🔴 Not started

    ❓ Open Questions

    • Fastboot bulk transfer root cause unknown. download/flash/boot all hang at “Sending” on v1.0–v1.6. No usbmon trace of an actual attempt exists. Next step: tools/verify/fastboot-usbmon.sh
    • Whether 5-press power sequence works (blue LED now visible — worth retesting)
    • Wacom stylus in TWRP (driver in kernel; not needed for basic UI interaction)
    • Monochrome theme + A2 refresh built into v3.4 but not visually verified on device yet
    • Whether aes-xts-plain64 software dm-crypt correctly decrypts ICE-encrypted userdata (v3.4, untested)
    • All fastboot getvar variables return FAIL (Varlist empty) — only partition-size:X works (separate GPT code path)

    📜 Critical Rules — Learned the Hard Way

    🧱 Boot Chain & EDL Access
    1. Never write BCB boot-recovery without (a) a confirmed-working image AND (b) a hardware escape route that does not depend on Android booting.
    2. Android boot is the only software path to adb reboot edl. Any action that prevents Android from booting severs EDL 9008 access entirely.
    3. All partition writes use EDL, not fastboot. Fastboot flash/boot hang at “Sending” — root cause unknown.
    4. Never flash the xbl partition. ABL failure → EDL drop (recoverable). XBL failure → hard brick, no recovery.
    5. EDL firehose: one edl-run invocation per Sahara session. But multiple writes within one invocation work fine. Always write co-dependent partitions (e.g. keymaster + userdata) in a single invocation — writing one and power-cycling breaks FDE with no software path back to 9008.
    6. Start edl-run before plugging in USB. PBL sends Sahara HELLO only once in a short window after enumeration.
    7. qcserial must be blacklisted (/etc/modprobe.d/no-qcserial.conf). Do not remove it — it consumes the HELLO packet within ~850 ms of enumeration.
    🔐 ABL, FDE & Signing
    1. VERIFIED_BOOT=1 causes 05c6:f000 (Qualcomm diagnostic mode). Magisk-patched boot fails AVB1 verification. Do not use.
    2. Cmdline injection alone does not fix FDE. vold’s key derivation depends on TrustZone keymaster RoT set via VBSendRot, not a kernel parameter.
    3. FDE RoT changes when switching between stock and custom ABL → factory reset required. Upgrading between custom ABL versions preserves RoT.
    4. Always use tools/abl-build <version> to build AND sign. Never build or sign manually.
    5. qtestsign MUST use -v 5 for SDM636. Default -v 3 produces a hash segment XBL silently rejects — device drops straight to EDL 9008 with no output. Caused hours of debugging.
    6. EnableDisplayMenu must be FALSE. EPDC hangs in UEFI context and blocks the USB event loop — fastboot never enumerates.
    7. No UEFI protocol calls safe from fastboot context. All boot-time detection must run in LinuxLoaderEntry() before EnterFastboot().
    8. UEFI NV variables do not survive watchdog resets. gRT->SetVariable(NON_VOLATILE) appears to succeed but data is lost on reset.
    🧠 MMIO & Hardware Register Access
    1. Never use raw MmioRead32/MmioWrite32 without GCD memory mapping. v2.0 incident: MmioRead32(0x03069004) on unmapped TLMM page → data abort → ABL crash → device bricked. Recovery required full battery drain + cold-boot PBL 9008 with EDL cable.
    2. TLMM flat range (0x03000000–0x030FFFFF) is XPU-blocked from ABL. Use south tile instead: 0x03100000 + (gpio × 0x1000). Confirmed in v2.3b diagnostic: flat hangs indefinitely, south tile reads succeed instantly.
    3. CNOC clocks are already running by ABL time — the TLMM stall is XPU access control, not clock gating. See Research: GCC/TLMM Clock Enable.
    📦 TWRP Build & Recovery
    1. TWRP ramdisk files go in recovery/root/ device tree overlay, not PRODUCT_PACKAGES (which targets the system image).
    2. TWRP 3.7.1 touch flags (RECOVERY_TOUCHSCREEN_SWAP_XY, TW_INPUT_FLIP_X/Y) are dead code — Soong never wires them to compiler flags. Must patch gui.cpp directly.
    3. Force-relink libminuitwrp.so after source changes. Delete recovery/root/system/lib64/libminuitwrp.so and obj/PACKAGING/recovery_intermediates/ between builds that modify minui/ebc code.
    4. Before flashing any new ABL, identify the last known-good version. The first 9008 session after a bad flash must restore the last known-good, not the untested new one.
    5. Update builds/abl/CHANGELOG.md or builds/twrp/CHANGELOG.md before flashing any new build. Every version needs: what changed, outcome, root cause if failed.