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.

More posts