Archives: Boox Knowledge Base

  • Session 31 — 2026-03-29b — A2 Waveform Kernel RE; NOP Patch Identified

    Session 31 — 2026-03-29b

    FieldValue
    Date2026-03-29
    Phase03b — TWRP (A2 waveform RE)
    Duration~3 h

    Transform Function Decoded

    onyx_waveform_mode_transform() at kernel offset 0x427a8c:

    • Loads 18-entry transform table from driver state (64 bytes per entry)
    • Compares waveform firmware version byte against each entry
    • If matched: replaces requested mode with table’s value (waveform v0x19 maps A2 → AUTO → GC16)
    • map_auto_mode() at 0x427bc8 maps AUTO (0xFF) → GC16 for full-screen updates

    Call Site and Patch

    FieldValue
    Patch offset0x41d410
    Original instructionBL 0x427a8c = 0x9400299F
    Patched instructionNOP = 0xD503201F

    Effect: all waveform modes pass through unchanged to EPDC hardware. AUTO (0xFF) still mapped by subsequent map_auto_mode(). Standard modes (DU, GC16, GC4, A2) are hardware-supported — the transform was overly conservative.

    SET_EBC_UPDATE_SCHEME — Dead Code

    Four scheme validation strings exist in the kernel binary but have zero ADRP+ADD references. The scheme-setting ioctl was never compiled in (or was eliminated by the linker). No userspace API exists to change the update scheme in this kernel build.

    Two Kernel Display Paths

    1. /dev/ebc ioctl (TWRP path): goes through onyx_waveform_mode_transform() → A2 blocked
    2. HWC/SurfaceFlinger (Android path): uses onyx_epdc_set_mode() → bypasses transform → A2 works

    Tools Created

    • tools/ebc-probe.c — comprehensive EBC ioctl prober, tests all ioctls and waveform modes
    • tools/patch-kernel-waveform-transform.py — NOPs the BL at 0x41d410; --dry-run verified against extracted kernel
  • Session 30 — 2026-03-29a — Hall Sensor Boot Mode Selection WORKING!

    Session 30 — 2026-03-29a

    FieldValue
    Date2026-03-29
    Phase03b2 — Boot mode selection
    Duration~2 h

    XPU Discovery: Flat TLMM is Blocked

    During oem test-sensor testing:

    • CNOC clocks already ON (all three CBCRs read 0x00000001) — XBL leaves them active through ABL
    • Flat TLMM: 0x03069000 (GPIO 105 flat) → HUNG (watchdog fired)
    • Flat TLMM: 0x03000000 (GPIO 0 flat) → HUNG
    • South tile: 0x03169000 (GPIO 105) → ✅ WORKS — reads 0x00000001 (cover open)
    • South tile: 0x03100000 (GPIO 0) → ✅ WORKS

    TrustZone XPU blocks ABL’s VMID from the flat decode window (0x03000000–0x03069FFF). The south tile range (0x03100000–0x03169FFF) is not blocked. Both map to the same physical registers.

    ABL v2.4 — Boot Mode Selection

    Reads GPIO 105 at 0x03169004 after GetKeyPress(). If bit 0 = 0 (cover closed), waits 3 seconds, re-reads. If still closed, sets BootIntoRecovery = TRUE.

    StateGPIO 105Result
    Cover openbit 0 = 1Normal Android boot
    Cover closedbit 0 = 0→ TWRP (display + touch + ADB)

    Both states confirmed working. v2.4 is the new last-known-good ABL.

    Phase 03b2 Status: COMPLETE

    • South tile address (0x03169004) is the permanent address for hall sensor reads from ABL
    • No clock enable needed — CNOC confirmed already running
    • 3-second debounce prevents accidental recovery entry
    • Watchdog-armed oem test-sensor kept for future diagnostics
  • Session 29 — 2026-03-28c — GCC CNOC Clock Research; ABL v2.3 Source Ready

    Session 29 — 2026-03-28c

    FieldValue
    Date2026-03-28
    Phase03b2 — Boot mode selection
    Duration~2 h

    GCC/CNOC Clock Architecture for SDM660/636

    No dedicated GCC_TLMM_AHB_CLK exists. TLMM sits on the CNOC (Config NOC) bus. Three CBCR clocks needed:

    ClockRegisterAddress
    GCC_SNOC_CNOC_AHB_CBCRGCC+0x40180x00104018
    GCC_CNOC_PERIPH_SOUTH_AHB_CBCRGCC+0x501C0x0010501C
    GCC_CNOC_PERIPH_NORTH_AHB_CBCRGCC+0x50200x00105020

    Source: boot_images/QcomPkg/Sdm660Pkg/Library/ClockTargetLib/ClockHWIO.h. CBCR enable protocol: write bit 0 = 1, poll bit 31 until 0. RMSK = 0x80000005. GCC base itself is safe to access from ABL (cannot be clock-gated by the clocks it manages).

    ABL v2.3 Design

    • Boot path: reverted to v1.6 equivalent — all hall sensor MMIO removed. Boot path is clean.
    • oem test-sensor: live GCC CNOC clock enable + TLMM GPIO read. If fastboot hangs, power-cycle recovers (no bootloop risk).
    • NV attempt counter and watchdog removed — unreliable (NV doesn’t survive resets). Not needed for fastboot-only approach.

    Source prepared, not yet built. Device needs PBL 9008 recovery first (still bootlooping on v2.2).

  • Session 28 — 2026-03-28b — TWRP v3.7 Stable; TLMM Clock Gating Discovery

    Session 28 — 2026-03-28b

    FieldValue
    Date2026-03-28
    Phases03b — TWRP, 03b2 — Boot mode selection
    Duration~5 h

    TWRP v3.6/v3.7 — Crypto Disabled

    Disabled TW_INCLUDE_CRYPTO in BoardConfig.mk; removed qseecomd and keymaster-4-0 services from init.rc. v3.6 confirmed: no FDE prompt in TWRP, no FDE prompt after reboot to Android. Clean boot cycle restored. v3.7 added A2 dither fix (0x1000) and WAIT_UPDATE.

    A2 Waveform Mode — Blocked by Kernel

    EPDC debug log shows: waveform_mode[2] is_convert[1] — the kernel’s onyx_waveform_mode_transform overrides our A2 (0x0c) → GC16 (2). Root cause: waveform file version 0x19 (panel ES103TD1C1) doesn’t list REAGL modes; driver calls "force use auto waveform mode, because reagl disabled!" and overrides all modes to auto. Fix requires either: kernel binary NOP patch at transform call site, or finding SET_EBC_UPDATE_SCHEME ioctl to switch to HANDWRITE scheme.

    TLMM Clock Gating — ABL MMIO Cannot Work

    ABL v2.2 (safe GCD mapping + watchdog + NV variable attempt counter) caused bootloop. Root cause: TLMM AHB clock is gated when ABL runs. MmioRead32 on clock-gated peripheral causes an indefinite bus stall (not a data abort). GCD mapping prevents the data abort but cannot prevent the stall. UEFI NV variables also do not survive watchdog resets on this device.

    Hall Sensor GPIO Confirmed (from Linux)

    Tested safely from TWRP:

    echo 105 > /sys/class/gpio/export
    # Cover open:  value = 1
    # Cover closed: value = 0

    !(val & 0x1) = closed is correct. ABL approach requires enabling GCC_TLMM_AHB_CLK first — not yet solved.

    Changelogs Created

    • builds/twrp/CHANGELOG.md — all 28 TWRP versions (v1.0–v3.7)
    • builds/abl/CHANGELOG.md — all ABL versions (v1.0–v2.2)
    • CLAUDE.md: mandatory changelog rule for all future ABL and TWRP builds
  • Session 27 — 2026-03-28a — ABL v2.0 Brick; TWRP Crypto Disabled

    Session 27 — 2026-03-28a

    FieldValue
    Date2026-03-28
    Phases03a, 03b, 03b2
    Duration~2 h

    ABL v2.0 — Brick

    Implemented hall sensor GPIO read using raw MmioRead32(0x03069004) in LinuxLoaderEntry. Built v2.0, signed -v 5, flashed via EDL. Device bricked. Red LED on, no USB enumeration, no 9008. Root cause: MmioRead32 on unmapped TLMM page → data abort → ABL crash. Only recovery: battery drain → PBL 9008.

    v2.0 binary deleted. v2.1 built with safe GCD memory mapping (gDS->GetMemorySpaceDescriptor + AddMemorySpace + SetMemorySpaceAttributes(EFI_MEMORY_UC)) and graceful fallback if mapping fails. CLAUDE.md constraint 10 added: never use raw MmioRead32 without GCD mapping.

    TWRP Crypto Root Cause

    Isolated FDE prompt cause without Magisk: Android boot → adb reboot recovery → cancelled TWRP password prompt (read-only) → rebooted → FDE prompt appeared. TWRP’s keymaster/TZ interaction during boot taints TrustZone state. Magisk is not the cause.

    Options evaluated: (1) skip crypto — simplest; (2) disable FDE via fstab; (3) fix TWRP keymaster integration — blocked by RoT binding, keystore2 SIGSEGV, no kernel source. Decision: option 1.

    TWRP v3.5 Footer Fix

    Patched cryptfs.cpp: replaced failed_decrypt_count increment + put_crypt_ftr_and_key call on error with rc = -1 (no footer write). After v3.5: failed_decrypt_count = 0 confirmed in footer. FDE prompt still appears — footer write was not the sole cause, but preventing it is still correct behavior.

    BCB Wipe Confirmed

    BCB contained: boot-recovery + recoveryn--wipe_datan--reason=CryptKeeper.MAX_FAILED_ATTEMPTS,20. TWRP does not process --wipe_data from BCB and does not clear BCB after boot — causes boot loop. Cleared manually with dd if=/dev/zero.

  • Session 26 — 2026-03-27i — FDE dm-crypt IV Root Cause; TWRP v3.4

    Session 26 — 2026-03-27i

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

    dm-crypt IV Root Cause

    Crypto footer at offset 0x24 of the last 16 KB of userdata:

    00000020  00 00 00 00 61 65 73 2d  78 74 73 00 00 00 00 00  |....aes-xts.....|

    crypto_type_name = "aes-xts" — Qualcomm HW FDE. TWRP’s create_crypto_blk_dev() passes this verbatim to DmTargetCrypt. Kernel dm-crypt requires cipher-chainmode-ivmode format. "aes-xts" has no IV mode → "IV mechanism required".

    Fix: Patch cryptfs.cpp to append -plain64: "aes-xts""aes-xts-plain64". Applied in v3.4. Hypothesis: ICE hardware XTS uses sector-number tweaks equivalent to plain64. If ICE uses a different IV scheme, decryption will produce garbage (read-only, no data corruption).

    ICE Hardware Confirmed

    • ICE driver: c0c8000.sdcc1ice (bound to eMMC)
    • QCE: /dev/qce at 1de0000.qcedev
    • Vendor libs: libcryptfshwcommon.so (exports set_ice_param, set_hw_device_encryption_key)
    • Kernel has req-crypt dm target but TWRP’s CONFIG_HW_DISK_ENCRYPTION path is compiled out (requires libcryptfs_hw)

    TWRP v3.4

    Contains: dm-crypt plain64 fix + health HAL service in init.rc + VINTF manifest + monochrome theme + A2 waveform. Built and flashed. Not yet tested in TWRP — Android showed unexpected FDE password prompt after EDL reset; user factory-resetting to restore.

    Possible cause of FDE prompt: session 24’s failed decrypt attempts incremented failed_decrypt_count in crypto footer (put_crypt_ftr_and_key called on error path). Consider guarding against footer writes on IV-related failures.

  • Session 25 — 2026-03-27h — Monochrome Theme & A2 Waveform Mode

    Session 25 — 2026-03-27h

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

    Monochrome Theme Fixes

    ElementBeforeAfter
    accent_color (header/tab text)#000000#D0D0D0
    Navbar fill (icons)#000000%accent_color%
    warning#F8F8A0 (yellow)#C0C0C0
    error#FF0101#000000
    highlight#0090CA#808080
    Cursor#FFFF00FF#000000FF
    Keyboard background (all 3 templates)#111111#D0D0D0
    Keyboard keys#111111#FFFFFF
    Keyboard text#EEEEEE#000000
    Splash background#222222#FFFFFF

    Ryogo-X overlay PNGs are black-on-transparent/white — work perfectly for e-ink without modification. Problems were purely in XML color definitions.

    A2 Waveform Mode

    Changed ebc_refresh() in graphics_fbdev.cpp:

    FieldBeforeAfter
    waveform_mode0xff (auto → GC16)0x0c (A2 — fast)
    update_mode0x10000x1003 (stock interactive)
    dither0x10000 (not needed for A2)

    A2 = fast refresh with basic grayscale. Gray UI elements (#D0D0D0 header) remain visible. DU would be faster but loses gray distinction. GC16 was too slow for interactive use.

    Boot Delay (~26 s)

    • ~5 s: icnss_fw_complete WiFi firmware timeout — compiled into kernel binary, cannot remove
    • Several seconds: IOMMU hardware init — requires kernel source to change
    • Remaining: TWRP loading 60+ pages, 24 languages, crypto services

    Cannot significantly reduce without kernel source. Deferred.

  • Session 24 — 2026-03-27g — Touch Fix; FDE Decrypt Chain (6/7 Layers)

    Session 24 — 2026-03-27g

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

    Touch Rotation Fix

    v3.2’s X-flip was wrong. Raw touch coordinates from getevent: ABS_MT_POSITION_X range 0–1871 (long axis), ABS_MT_POSITION_Y range 0–1403 (short axis) — landscape orientation. The correct fix is a full 90° rotation applied in gui.cpp after vk_tp_to_screen scaling:

    int px = x, py = y;
    int W = gr_fb_width() - 1;   // 1403
    int H = gr_fb_height() - 1;  // 1871
    x = py * W / H;              // swap: Y→X, scale H→W
    y = H - px * H / W;          // swap: X→Y, invert, scale W→H

    Physical orientation: raw_X 1871=TOP, 0=BOTTOM; raw_Y 0=LEFT, 1403=RIGHT. Applied in v3.3.

    FDE Decrypt Dependency Chain

    #LayerFixStatus
    1Vendor partition not mountedMount vendor in init.rcFixed
    2qseecomd not runningAdd qseecomd service to init.rcFixed
    3keymaster@4.0 not startedAdd keymaster HAL service to init.rcFixed
    4libion.so missing from ramdiskAdd to recovery/root/system/lib64/Fixed
    5VINTF manifest missingMinimal /system/etc/vintf/manifest.xmlFixed (manual push, not yet in build)
    6health@2.0 HAL not startedStart TWRP’s built-in health HALFixed (manual test)
    7dm-crypt “IV mechanism required”Unknown — IV mode missing from cipher stringBLOCKER

    dm-crypt IV error (Blocker)

    Kernel 4.4.194 reports device-mapper: table: 252:0: crypt: IV mechanism required. TWRP’s cryptfs.cpp (AOSP 12.1 vold) passes a cipher spec without IV mode (e.g., aes-xts instead of aes-xts-plain64). The kernel requires cipher-chainmode-ivmode format. Fix: patch cryptfs.cpp to append IV mode to cipher string.

    VINTF Manifest Discovery

    AOSP 12.1’s servicemanager requires /system/etc/vintf/manifest.xml to validate service registration. Without it, keystore2 fails with UNKNOWN_ERROR. This check did not exist in earlier Android versions.

  • Session 23 — 2026-03-27f — Display Fix! Waveform Firmware Missing from Ramdisk

    Session 23 — 2026-03-27f

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

    Display Root Cause: Missing Waveform Firmware

    Comparing dmesg from Android boot vs TWRP boot revealed the smoking gun:

    Android: fw->size=282231  mode0 is complete
    TWRP:   Vendor Can't find firmware  mode0 timeout

    The EPDC needs the e-ink waveform firmware file at /waveform/eink_waveform.wbf (276 KB). In Android, system-as-root provides this via /system/waveform/. In TWRP, the ramdisk is root and the path does not exist. All prior ioctl attempts (v1.7–v2.5) were correct in userspace — but the EPDC hardware could not process them without its waveform lookup table.

    Fix: Add file to device/onyx/noteair1/recovery/root/waveform/eink_waveform.wbf. TWRP v2.6 → first ever working e-ink display in custom recovery on this device.

    Touch Rotation Investigation (v2.6–v3.2)

    Touch hardware works immediately but coordinates are rotated. TWRP 3.7.1 RECOVERY_TOUCHSCREEN_SWAP_XY/FLIP_X/FLIP_Y flags in BoardConfig.mk are dead code — the Soong .go generator never wires them to compiler defines. TW_INPUT_FLIP_X/Y doesn’t exist in 3.7.1. Only option: patch gui.cpp or events.cpp directly in the source overlay.

    v3.1 captured corner tap data (LOGINFO): top-left tap → x=1359, y=56; bottom-right → x=24, y=1822. This indicates X is inverted. v3.2 applied X-flip in gui.cpp — user reported still incorrect (needed re-verification).

    Other Findings

    • ebc buffer initialised to 0xFF (white) after mmap — prevents stale boot logo artifacts
    • Display takes ~27 s from boot to visible output (icnss_fw_complete 5 s timeout + IOMMU delay)
    • Brightness path corrected in BoardConfig: /sys/devices/soc/c1b6000.i2c/i2c-6/6-0038/backlight/onyx_bl_br/brightness
    • FDE decrypt: TWRP shows decrypt prompt; success not yet confirmed
  • Session 22 — 2026-03-27e — Strace Stock Recovery; Display Problem is Kernel/Init

    Session 22 — 2026-03-27e

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

    Strace Setup

    • Static aarch64 strace v6.7 pushed to /tmp/
    • Full stock recovery ramdisk extracted and pushed as /tmp/stockroot/
    • Chroot with bind mounts for /dev, /proc, /sys
    • TWRP recovery SIGSTOP’d (PID 528) before running stock binary

    Stock Recovery Display Init Sequence (Confirmed)

    1. openat("/dev/graphics/fb0", O_RDWR|O_CLOEXEC) → fd 0
    2. ioctl(0, FBIOGET_FSCREENINFO)
    3. ioctl(0, FBIOGET_VSCREENINFO)
    4. mmap(NULL, 21209088, PROT_READ|PROT_WRITE, MAP_SHARED, 0, 0)  ← full double-buffer
    5. openat("/dev/graphics/fb0", O_RDWR) → fd 12  (ebc geometry helper)
    6. ioctl(12, FBIOGET_VSCREENINFO / FBIOGET_FSCREENINFO)
    7. close(12)
    8. openat("/dev/ebc", O_RDWR) → fd 12
    9. mmap(NULL, 10513152, PROT_READ|PROT_WRITE, MAP_SHARED, 12, 0)
    10. ioctl(12, 0x700c, ...)  ← EBC_SEND_UPDATE (init/clear) — BEFORE blank
    11. ioctl(0,  FBIOBLANK, 4)  ← FB_BLANK_POWERDOWN
    12. ioctl(0,  FBIOBLANK, 0)  ← FB_BLANK_UNBLANK
    13. ioctl(12, 0x700c, ...)  ← EBC_SEND_UPDATE (content) ×2

    Stock code never calls: FBIOPUT_VSCREENINFO, FBIOPAN_DISPLAY, EBC_WAIT_UPDATE, EBC_CLEAR, or any sysfs writes.

    Critical Finding: Stock Binary Also Failed to Render

    The stock recovery binary ran its complete display init sequence inside the TWRP chroot and produced no visible display change. This definitively proves:

    • The EPDC display problem is NOT a userspace ioctl issue
    • Our TWRP ioctl code was correct all along
    • The problem is in the kernel or init boot environment

    Two remaining hypotheses

    1. fd conflict — TWRP was only SIGSTOP’d, still holding open mmaps to fb0 and /dev/ebc. EBC driver may only support one active client; stock binary’s ioctls silently no-op’d.
    2. Missing hardware init in TWRP’s init.rc — stock recovery ramdisk’s init scripts may perform MDSS/EPDC hardware initialization that TWRP’s init.rc doesn’t replicate.

    Input Device Map (Confirmed from Strace)

    DeviceType
    event0Power/Back button (KEY_POWER, KEY_BACK)
    event1Capacitive touchscreen (MT type B)
    event2EMR stylus (ABS_X/Y/PRESSURE/DISTANCE)
    event3USB mouse
    event4Virtual keys / GPIO
    event5Hall sensor (KEY_LEFTALT, KEY_POWER, KEY_WAKEUP)
    event6Volume up

    Touchscreen uses standard Linux MT type B — TWRP handles natively.

    Next Step

    Fully SIGKILL TWRP (prevent init respawn) and re-run stock binary to eliminate fd conflict. Compare stock recovery init.rc against TWRP’s for missing MDSS/EPDC hardware init writes.