Archives: Boox Knowledge Base

  • Session 21 — 2026-03-27d — Ghidra EBC RE; TWRP v1.7–v2.5 EPDC Attempts

    Session 21 — 2026-03-27d

    FieldValue
    Date2026-03-27
    Phase03b — TWRP
    Duration~2.5 h

    EBC Ioctl Numbers — Confirmed via Ghidra

    IoctlValuePurpose
    EBC_SEND_UPDATE0x700cTrigger EPDC refresh
    EBC_WAIT_UPDATE0x700dWait for completion
    EBC_CLEAR0x700eClear screen

    Update struct (40 bytes, passed to 0x700c)

    struct onyx_epdc_update {
        int32_t y;             // offset 0
        int32_t x;             // offset 4
        int32_t width;         // offset 8
        int32_t height;        // offset 12
        int32_t waveform_mode; // offset 16: 1=DU, 2=GC16, 0xff=auto
        int32_t update_mode;   // offset 20: from mode >> 5
        int32_t marker;        // offset 24: sequence counter
        int32_t dither;        // offset 28: 0x28 or 0x1000
        uint32_t flags;        // offset 32: 0x20000 (stock)
        int32_t reserved;      // offset 36: 0
    };

    Stock code never calls FBIOPUT_VSCREENINFO or FBIOPAN_DISPLAY. Flow: syncDataToExtendedBuffer (row-by-row copy to ebc mmap, pixel_stride=7488 not row_bytes=7552) → refreshScreen (ioctl 0x700c).

    TWRP v1.7–v2.5 Iterations

    VersionChangeResult
    v1.7ebc open + ioctl + buffer copyDouble buffer; hung on first flip
    v1.8Forced single buffer, hardcodedSIGSEGV — stride mismatch (7552 vs 7488)
    v1.9Skip ebc buffer copy, ioctl onlyNo crash. Kernel receives ioctl. No display change.
    v2.0–v2.4Row-by-row copy; test patterns; stock mode values; FBIOBLANKAll: ioctl accepted, logged by kernel, no display change
    v2.5Added FBIOPUT_VSCREENINFO yoffset=0Built but session ended

    Kernel log: SET_EBC_SEND_UPDATE -- magic[N] [x=0 y=0 w=1872 h=1404]! flags=0x20000! — the ioctl entry point runs and logs parameters, but no pixels reach the panel. Problem is below the ioctl dispatch — in MDSS pipeline state, onyx_epdc_put_image, or _epdc_update_wb_direct.

    Build System Learnings

    • Added device/onyx/noteair1/src-overlay/ mechanism — files copied over TWRP source before build; updated tools/twrp-build accordingly
    • RECOVERY_GRAPHICS_FORCE_SINGLE_BUFFER flag unreliable with Soong cache — hardcode in source overlay instead
    • Must delete stale libminuitwrp.so in recovery ramdisk staging and *relink* files before each build

    Conclusion

    Trial-and-error ioctl parameter tuning is exhausted. Strace of the stock recovery binary is the definitive next step to find what initialization step we are missing.

  • Session 20 — 2026-03-27c — TWRP v1.6: libresetprop.so Fix; EPDC RE

    Session 20 — 2026-03-27c

    FieldValue
    Date2026-03-27
    Phase03b — TWRP
    Duration~1.5 h

    Root Cause: Missing libresetprop.so

    Running recovery binary manually revealed the real crash cause — not the /dev/fb0 path:

    CANNOT LINK EXECUTABLE: library "libresetprop.so" not found

    libresetprop.so (from Magisk’s resetprop, built by external/magisk-prebuilt) was compiled to the system image output directory but never copied into the recovery ramdisk. The dynamic linker failed instantly — before any graphics init, before any log creation — explaining why /tmp/recovery.log never appeared in v1.0–v1.5.

    Fix: Copy the built library to device/onyx/noteair1/recovery/root/system/lib64/libresetprop.so. Files in that directory are merged directly into the recovery ramdisk. PRODUCT_PACKAGES does NOT work for recovery ramdisk inclusion — that targets the system image.

    TWRP v1.6 — First Stable Recovery

    AreaStatus
    Recovery binaryRunning (PID 533) — stable
    FramebufferDetected: 1872×1404, 32bpp RGBA, double-buffered
    UIFully loaded — splash + main theme + 60+ pages + 24 languages
    PartitionsFstab processed; cache mounted; data encrypted (decrypt prompt)
    BrightnessFound at correct i2c path (TW_BRIGHTNESS_PATH in BoardConfig.mk is wrong)
    DisplayBlank — hung in onyx_epdc_wait_all_update_compelete
    keystore2Crash-looping (SIGSEGV) — not blocking TWRP

    E-ink Display Architecture

    Two display paths exist:

    1. /dev/graphics/fb0 — standard Linux framebuffer. TWRP writes pixels here. Pipeline is initialised (MDSS DSI → TC358762 → EPDC → MAX17135), but EPDC update completion never fires. Recovery hangs in onyx_epdc_wait_all_update_compelete.
    2. /dev/ebc (char 10,35) — Onyx-proprietary EPDC control device. Stock recovery uses this exclusively via librecovery_ui.so‘s FBDev class. This is the reliable path.

    Stock librecovery_ui.so — Key Symbols

    SymbolPurpose
    FBDev::openEpdcFd() @ 0x4ec50Opens /dev/ebc
    FBDev::refreshScreen(x,y,w,h,mode) @ 0x4ee9cTriggers EPDC refresh via /dev/ebc ioctl
    FBDev::syncDataToExtendedBuffer()Copies pixel data to EBC extended buffer
    MinuiBackendFbdev::Flip()Page flip + EPDC refresh trigger

    Next Steps

    1. Try quick sysfs unblock: write to /sys/devices/sepdc/submit_upd_work or toggle update_disable from ADB shell
    2. If that fails: Ghidra on librecovery_ui.so to extract /dev/ebc ioctl numbers from FBDev::refreshScreen
    3. Patch TWRP minui (graphics_fbdev.cpp) to open /dev/ebc and issue refresh ioctl
  • Session 19 — 2026-03-27b — ABL Source Cleanup; Flashing-Fix Code Removed

    Session 19 — 2026-03-27b

    FieldValue
    Date2026-03-27
    Phase03a — Custom UEFI ABL
    Duration~30 min

    What Happened

    ABL source had accumulated speculative fastboot flashing-fix changes across v1.4–v1.6 (gRxBuffer DMA redirect, buffer size reductions, SetMem zeroing, FASTBOOT_PLACEHOLDER_SIZE). None of these resolved the bulk transfer hang. This session reviewed all 18 prior diary entries, classified every change as “advancement” (keep) or “flashing fix attempt” (remove), and surgically reverted three files to upstream.

    Final Clean State vs Upstream

    FileChangeSource version
    BaseTools/Conf/tools_def.template-Wno-misleading-indentationv1.0
    BaseTools/Source/C/Makefiles/header.makefile-Wno-stringop-truncationv1.0
    Conf/tools_def.txt-Wno-misleading-indentationv1.0
    LinuxLoader.cVBSendRot + VBCmdLine stock overridev1.3
    LinuxLoader.infgEfiQcomVerifiedBootProtocolGuidv1.3
    FastbootCmds.cCmdOemRebootEdl + registration onlyv1.3
    QcomModulePkg.dscEnableDisplayMenu|FALSEv1.3

    FastbootCmds.h and FastbootMain.c are now fully identical to upstream — no diff at all.

    Removed Code Summary

    VersionWhat was removed
    v1.4gRxBuffer DMA redirect in CmdDownload; USB_BUFFER_SIZE 16→64 MB
    v1.5gRxBuffer SetMem zeroing; continued redirect
    v1.6Buffer reductions (256→34 MB); FASTBOOT_PLACEHOLDER_SIZE; mFlashDataBuffer override

    Root cause of fastboot bulk transfer hang remains below ABL level — likely XBL USB driver (TRB ring overflow). Documented in phase 03a doc for future investigation.

    Next Step

    Build clean source as v2.0 (new major version — clean slate). Flash and verify Android boots + fastboot enumerates + oem reboot-edl works.

  • Session 18 — 2026-03-27a — TWRP v1.4 First Boot Confirmed; LED Discovery

    Session 18 — 2026-03-27a

    FieldValue
    Date2026-03-27
    Phases03a — Custom UEFI ABL, 03b — TWRP
    Duration~1.5 h

    LED Discovery

    The LED indicator is a tiny pinhole in the power button bezel — it had been completely blocked by dust. After cleaning with a needle: blue and red LEDs are clearly visible. Green sysfs write succeeds (value 255 confirmed) but produces no visible light — two-color physical LED only. Prior sessions concluded “LEDs useless and 5-press sequence may not work” — both conclusions were wrong.

    LED boot-stage scheme (adopted)

    LED stateMeaning
    BlueEarly-init reached
    DarkInit stage (green invisible)
    RedFilesystem mounted
    Red + BlueADB ready (FunctionFS BIND)
    NothingKernel never started

    TWRP v1.4 — First Confirmed Boot

    With LED pinhole clean, retested TWRP v1.4. Observed red+blue LED sequence. USB enumerated as 18d1:d001. ADB connected: device ID 6db6dcde recovery. First custom recovery successfully running on this device.

    Hardware confirmed initialised in TWRP

    • 8 CPUs online, eMMC accessible
    • Display pipeline: MDSS DSI → tc358762 → EPDC → MAX17135
    • Touch: cyttsp5 | Pen: Wacom EMR | Hall sensor | Frontlight: LM3630A
    • USB / ADB operational

    recovery Binary Crash-Loop

    The recovery binary starts and immediately exits with status 1, restarting every ~5 s. No /tmp/recovery.log written. Root cause: TWRP’s minui opens /dev/fb0 but this kernel only creates /dev/graphics/fb0. Fix applied in v1.5: symlink /dev/graphics/fb0 /dev/fb0 in init.recovery.qcom.rc on early-init.

    Even after the symlink fix, EPDC update counters are zero — no framebuffer updates submitted. Display will likely remain blank; explicit EPDC ioctl/sysfs refresh required. Userdata reads as all zeros in crypto footer — unencrypted post factory-reset.

    ABL v1.6 — Buffer Reduction

    Reduced total UEFI buffer allocations from 256 MB to 34 MB: gRxBuffer 64→32 MB, gTxBuffer 64→1 MB, FastBootBuffer 128→1 MB. Theory: pool exhaustion caused FastbootPublishVar and CmdDownload DATA response failures. Built and signed -v 5. Ready to flash.

    New Tools

    • tools/twrp-build — new script matching abl-build style
    • tools/abl-build — improved: clean subcommand, -j1 auto-retry on parallel build failure, non-interactive mode
  • Sessions 16–17 — 2026-03-26b/c — ABL v1.5/TWRP v1.4 Fastboot Debug

    Sessions 16–17 — 2026-03-26b/c

    FieldValue
    Dates2026-03-26
    Phases03a — Custom UEFI ABL, 03b — TWRP
    Duration~3.5 h combined

    What Happened

    • Discovered ABL v1.5 was signed with qtestsign default -v 3 → device dropped straight to EDL 9008. Re-signed with -v 5; SHA256 changed from 64e9ad68… to 15be12dc…
    • Extended tools/abl-build: mandatory version arg, auto -v 5 signing, overwrite protection — eliminates manual signing errors
    • Flashed ABL v1.5 + pristine keymaster + pristine userdata in one EDL session (comma-separated partition list)
    • Built TWRP v1.4: TW_EXCLUDE_DEFAULT_USB_INIT, TW_INCLUDE_CRYPTO, buildvariant=eng — first attempt failed (TARGET_HW_DISK_ENCRYPTION requires libcryptfs_hw, dropped)
    • Captured full usbmon trace during fastboot session — confirmed two bugs
    • Session 17: flashed TWRP v1.4 to recovery, booted via adb reboot recovery — 5-min hang then fell back to Android; no USB, no display, no LEDs observed
    • Corrected VBSendRot compatibility claim: custom ABL’s RoT differs from stock ABL’s RoT — factory reset required on stock↔custom switch, but NOT between custom versions

    usbmon Findings

    Bug 1 — CmdDownload never sends DATA response

    Host→Dev  "download:00001000"  → delivered (Bo status 0)
    Host arms Bi (256 bytes) waiting for DATA response…
    … 15 SECONDS OF SILENCE — DEVICE NEVER RESPONDS …
    Bi completes: error -2 (ENOENT), 0 bytes → timeout
    Host→Dev  "getvar:serialno"   → error -108 (ESHUTDOWN) — endpoint dead

    The host correctly delivers the download command. The device receives it. But CmdDownload never sends the DATA acknowledgement. Root cause suspected: UEFI pool exhaustion from 256 MB total buffer allocations (FastBootBuffer 128 MB + gTxBuffer 64 MB + gRxBuffer 64 MB).

    Bug 2 — Varlist empty; all getvar variables FAIL

    Every getvar except partition-size (which uses a separate GPT-query path) returns FAILGetVar Variable Not found. FastbootPublishVar calls in FastbootCommandSetup are silently failing — likely same root cause as above (pool exhaustion leaves Varlist pointer corrupted).

    Decisions

    • tools/abl-build now handles full pipeline — never sign ABL manually again
    • Dropped TARGET_HW_DISK_ENCRYPTION from TWRP; TW_INCLUDE_CRYPTO software path retained
    • Added CLAUDE.md constraints 8–9: always use tools/abl-build; qtestsign -v 5 is mandatory
  • Session 15 — 2026-03-26a — ABL v1.3: VBSendRot + oem reboot-edl

    Session 15 — 2026-03-26a

    FieldValue
    Date2026-03-26
    Phase03a — Custom UEFI ABL
    Duration~2 h

    What Happened

    • Built v1.3 with three changes: VBSendRot() call added, EnableDisplayMenu=FALSE, and oem reboot-edl fastboot command handler
    • Factory reset performed to clear corrupted FDE/FBE keymaster state from previous experiments
    • v1.3 boots Android successfully — no FDE loop, no AVB failure
    • Confirmed EDL one-write-per-Sahara-session rule: after flashing one partition in a 9008 session, PBL closes the Sahara channel; a second flash in the same session silently fails or causes corruption
    • Removed dead v1.2 cmdline injection code from source

    Key Findings

    EDL One-Write-Per-Sahara-Session

    PBL 9008 Sahara protocol terminates the session after one successful partition write. Attempting a second write in the same session (without power-cycling) results in silent failure or corruption. Always power-cycle between EDL flash operations targeting different partitions.

    VBSendRot is required for clean boot

    VBSendRot() (Verified Boot Send Rot = Root of Trust) must be called before handing off to the kernel. Without it, the keymaster does not receive the verified boot state and subsequent FDE/FBE operations fail.

    EnableDisplayMenu=FALSE

    The stock ABL shows a 5-second “press any key to enter fastboot” menu on every boot. Setting EnableDisplayMenu=FALSE suppresses this — the device boots straight to Android without waiting.

    ABL v1.3 Change Summary

    ChangeEffect
    +VBSendRot()FDE/FBE keymaster initialised correctly
    EnableDisplayMenu=FALSENo fastboot menu delay on normal boot
    +oem reboot-edl commandSoftware EDL trigger via fastboot
    −v1.2 cmdline injectionDead code removed; no functional change
  • Session 14 — 2026-03-25a — EDL Timing & qcserial Blacklist

    Session 14 — 2026-03-25a

    FieldValue
    Date2026-03-25
    Phase03a — Custom UEFI ABL
    Duration~2 h

    What Happened

    • Debugged inconsistent EDL connection failures
    • Identified qcserial kernel module claiming 05c6:9008 before edl tool connects
    • Permanently blacklisted qcserial in /etc/modprobe.d/ — eliminates the race
    • Measured PBL Sahara HELLO window: approximately 1–2 seconds after USB enumeration
    • Established procedure: start edl-run first, then plug in cable
    • Created tools/sahara-probe utility to test connection timing
    • Confirmed: competing USB connections (e.g. two edl processes, or qcserial + edl) crash the PBL USB state machine — device must be power-cycled to recover

    Key Findings

    Correct EDL Connection Procedure

    # 1. Ensure qcserial is blacklisted (one-time setup)
    echo "blacklist qcserial" | sudo tee /etc/modprobe.d/no-qcserial.conf
    sudo modprobe -r qcserial
    
    # 2. Start edl tool BEFORE plugging in
    edl --serial /dev/ttyUSB0 ...  # or equivalent
    
    # 3. Plug in EDL cable
    # PBL sends HELLO within ~1-2 s; edl tool must already be listening

    Competing connections are fatal

    If two processes attempt to claim the 9008 device simultaneously, PBL’s USB state machine enters an unrecoverable state. The device becomes unresponsive on USB until power-cycled (battery drain or D+/GND reset). Always ensure only one process has the USB handle.

    Artifacts

    ArtifactNotes
    tools/sahara-probeUtility to test Sahara connection timing
    /etc/modprobe.d/no-qcserial.confPermanent qcserial blacklist on host
  • Session 13 — 2026-03-24a — EDL Cable Arrives; ABL v1.0–v1.2

    Session 13 — 2026-03-24a

    FieldValue
    Date2026-03-24
    Phase03a — Custom UEFI ABL
    Duration~3 h

    What Happened

    • EDL hardware cable arrived — first hardware EDL session
    • BCB loop (leftover from previous test) escaped via EDL
    • Ground-truth kernel cmdline captured — required su -c cat /proc/cmdline (not accessible without root)
    • Flashed v1.0 → FDE boot loop: KeyMasterSetRotAndBootState not called, FDE password prompt repeats
    • Built v1.1 with KeyMasterSetRotAndBootState call added → USB shows 05c6:f000 (fastboot), but AVB fails on Magisk-patched boot partition
    • Built v1.2 with cmdline injection attempt → confirmed dead code: VBCmdLine is already populated by PBL before ABL runs; ABL cannot override it

    Key Findings

    EDL access depends on ABL

    The software EDL trigger (adb reboot edl) works only when Android is running. If a broken ABL prevents Android from booting, only the hardware EDL cable provides 9008 access. The EDL cable is therefore a prerequisite for any ABL flashing experiment.

    v1.0: FDE loop root cause

    KeyMasterSetRotAndBootState() must be called to unlock the FDE keymaster. Without it, the device requests the disk password on every boot and loops when no password is entered.

    v1.1: AVB1 fails on Magisk-patched boot

    With KeyMaster fixed, the device enters fastboot (05c6:f000) but AVB verification fails because Magisk modifies boot.img. The fix is to disable AVB or add the patched hash.

    ABL Version Log

    VersionChangeOutcome
    v1.0Baseline compileFDE boot loop
    v1.1+KeyMasterSetRotAndBootState05c6:f000; AVB1 fails
    v1.2+cmdline injection (dead code)No change — VBCmdLine pre-set by PBL
  • Session 11 — 2026-03-23c — ABL Build Environment & v1.0

    Session 11 — 2026-03-23c

    FieldValue
    Date2026-03-23
    Phase03a — Custom UEFI ABL
    Duration~2 h

    What Happened

    • Cloned Qualcomm ABL source tree: LA.UM.7.2.c25 (the SDM636 branch used by Onyx)
    • Built Docker-based build environment with correct NDK/GCC toolchain
    • First successful ABL compilation — v1.0 binary produced
    • Signed with qtestsign -v 5 — critical: -v 3 (default) produces a hash segment silently rejected by XBL on SDM636
    • Phase directory renamed: 10-custom-abl.md03a-custom-abl.md, TWRP phase 0303b

    Key Findings

    qtestsign -v 5 is mandatory

    SDM636 XBL requires signing version 5. The default -v 3 is accepted without error by qtestsign but the resulting ELF is silently rejected by XBL at boot — device drops straight to EDL 9008 with no diagnostic output. The tools/abl-build script enforces -v 5 automatically; never sign manually.

    Artifacts

    ArtifactNotes
    builds/abl/abl-v1.0.elfFirst compiled ABL; untested on device (EDL cable not yet arrived)

    Next Step

    EDL cable ordered — arrival awaited before v1.0 can be flashed and tested.

  • Session 10 — 2026-03-23b — ABL Analysis; Ghidra RE Env; Custom ABL Phase Created

    Session 10 — 2026-03-23b

    Duration: ~3 hours · Phase: 03a (Custom ABL) created this session · Offline


    Key Findings

    • qtestsign confirmed: Stock ABL signed with Qualcomm test keys (“Generated Test Root CA”). Custom ABL with qtestsign will be accepted by XBL.
    • Fastboot hang: CmdDownload sends DATA response but USB BULK OUT endpoint may not be armed for the download. A custom ABL replacing the fastboot stack will fix this.
    • lk2nd not applicable: Device uses UEFI ABL (kernel=uefi), not legacy LK. lk2nd targets LK-based devices.
    • Ghidra RE Docker environment created (tools/docker/ghidra-re/, tools/ghidra-run)
    • TWRP phase sidelined pending Phase 03a completion