Tối 1/11 tôi cầm chiếc Redmi Note 11 cũ (mã spes, Snapdragon 680) và đặt một câu hỏi hơi điên: nếu đã có người chạy được iOS 16 trên Redmi Note 12 4G, thì máy của mình có chạy được không? Hai mươi sáu tiếng sau, máy lên màn hình khoá iOS, vuốt mở được, vào Settings được, và giao diện được vẽ bằng GPU Adreno 610 qua Mesa. Bài này ghi lại hành trình đó, theo đúng thứ tự mọi chuyện xảy ra, kể cả những lần panic.
Repo: tuanha1305/ios-topaz, nhánh
spes, fork từ MakrSas/ios-topaz.Lưu ý trước khi đọc: đây là một dự án nghiên cứu. Image hệ điều hành của Apple không được phát hành lại ở bất kỳ đâu; root filesystem được dựng trong CI từ IPSW tải thẳng từ máy chủ Apple và chỉ tồn tại dưới dạng mảnh đã mã hoá. Bài không phải hướng dẫn cài đặt cho người dùng cuối, và việc ghi đè
userdatasẽ xoá sạch Android.
Toàn bộ quá trình tôi làm cùng Claude Code (một AI agent chạy trong terminal): nó đọc code, viết patch kernel, điều khiển fastboot/adb, đọc log; còn tôi cầm máy, bấm nút, chụp màn hình và quyết định những bước không thể quay lại. Cách làm việc này đáng kể riêng, tôi nói thêm ở cuối bài.
1. Xuất phát điểm: ios-topaz
ios-topaz là một dự án port iOS 16.7 (userland của iPhone X, iPhone10,3) lên Redmi Note 12 4G (topaz). Không có iBoot, không có SEP, không có chip Apple nào cả. Thay vào đó là:
- Một shim bare-metal được đóng gói thành Android boot image v4. ABL (bootloader của Qualcomm) nạp nó như một kernel Linux bình thường qua
fastboot boot, shim chuyển sang nhân Gold, dựng GIC/timer rồi nhảy vào XNU. - XNU (kernel mã nguồn mở của Apple) được vá hơn 900 patch: driver UFS, màn hình qua MDP/DSI, cảm ứng, phím, IOSurface tự viết, AMFI/sandbox thay thế, AppleKeyStore giả…
- Một ADT (Apple Device Tree) tổng hợp, mượn node
/producttừ device tree thật của iPhone X. - Root filesystem iOS dạng HFS+ nằm trong phân vùng
userdata.
Phần GPU dựa trên mã nguồn của HackiOS (port iOS 16 lên POCO X5 Pro): Mesa freedreno nằm sau renderer OpenGL ES của CoreAnimation.
Ban đầu tôi muốn nhắm tới bản HackiOS cho Xiaomi 15 chạy iOS 26. Nhưng câu trả lời ngắn gọn là không thể: userland iOS 26 chỉ có bản arm64e, dùng Pointer Authentication (braa, retab, pacia…) ở gần như mọi hàm. Snapdragon 680 là ARMv8.0, không có PAC — các lệnh đó là undefined instruction. Topaz có thể giả lập LSE/LDAPR trong trap handler vì userland iPhone X chỉ thỉnh thoảng dùng tới; giả lập PAC cho từng lời gọi hàm thì không khả thi. Vậy nên đích đến là iOS 16.7, arm64 thường.
Vì sao spes là “anh em ruột” của topaz
| Redmi Note 11 (spes) | Redmi Note 12 4G (topaz) | |
|---|---|---|
| SoC | SM6225 (Snapdragon 680) | SM6225 |
| CPU | 4× Cortex-A73 2.4 GHz + 4× A53 | giống hệt |
| GPU | Adreno 610 | Adreno 610 |
| Màn hình | 1080×2400 | 1080×2400 |
| RAM | 4 GB, 0x40000000–0x140000000 | 4 GB + bank cao ở 0x1_40000000 |
| Cảm ứng | FocalTech FT3518, I2C 0x38, QUP0 SE2 | FocalTech, I2C 0x38 |
Cùng SoC nghĩa là phần lõi (CPU, GIC, timer, UFS, MDP) gần như dùng lại nguyên. Khác biệt nằm ở bản đồ RAM và dây nối của cảm ứng — và hoá ra hai chỗ đó tốn gần hết thời gian.
2. Tổng quan đường boot
flowchart TD
A["fastboot boot shim-boot.img<br/>(chỉ trong RAM, không ghi flash)"] --> B["ABL của Qualcomm<br/>nạp boot image v4"]
B --> C["Shim bare-metal<br/>màn hình đỏ · đánh thức nhân Gold qua PSCI<br/>dựng GIC, timer"]
C --> D["XNU + ADT tổng hợp<br/>RAM: 0xA0000000 + 2.5 GiB"]
D --> E["Driver sweet/topaz<br/>UFS · MDP/DSI · cảm ứng · phím · IOSurface"]
E --> F["rd=ufsroot0<br/>HFS+ 16 GB trong userdata"]
F --> G["launchd → backboardd → SpringBoard"]
G --> H["CoreAnimation → EAGL giả → Mesa freedreno<br/>→ RedwoodGPU → Adreno 610"]
Một điểm quan trọng: mọi lần thử đều là fastboot boot, kernel chỉ sống trong RAM. Phân vùng boot của Android không bị đụng tới. Thứ duy nhất được ghi vĩnh viễn là userdata (chứa root iOS), và việc đó chỉ làm sau khi kernel đã chứng minh chạy được tới bước mount root.
3. Tối 1/11: màn hình đỏ và dòng panic đầu tiên
18:45 — Kiểm tra bootloader. getprop trong Android báo flash.locked=1, trạng thái green. Hơi hoảng, vì mở khoá Xiaomi thường phải chờ 168 giờ. Nhưng vào Fastboot đọc thẳng thì:
unlocked: yes
Device critical unlocked: true
ROM custom aosp_spes tôi đang dùng đã giả các thuộc tính đó để qua Play Integrity. Bootloader thật ra đã mở từ lâu.
18:53 — Boot thử shim của topaz. Màn hình chuyển đỏ, hiện SHIM, MMFR0, SPEED silver=… gold=…. Ba điều được xác nhận cùng lúc: ABL của spes nhận boot image v4, framebuffer splash nằm đúng ở 0x5c000000 như topaz, và shim đánh thức được nhân Gold.
19:00 — Boot XNU bản CI của topaz, ADT tự dựng với file DeviceTree D22AP lấy riêng từ IPSW (chỉ 33 KB, tải bằng remotezip, không tải cả IPSW). Dòng đầu tiên hiện lên:
debug_log_init: Error!! gPanicBase is still not initialized
Nghe như lỗi, nhưng thực ra là tin tốt: chính XNU in dòng này. Kernel tìm vùng RAM lưu panic log (/pram), không thấy thì bỏ qua. Topaz cũng in dòng này ở mọi lần boot.
Ảnh chụp tiếp theo cho thấy XNU chạy khá sâu: VM bootstrap done, cpu=0 mpidr=0x100 came alive, gold perf state 6 (2400 MHz), IOKit, BSD, MDP VIG0 src 0x5c000000 … direct scanout ready. Rồi đứng hình.
Lần đầu XNU chạy trên spes. Console in hết màn hình thì quay lại từ trên xuống, nên nửa trên là dòng mới nhất (direct scanout ready), nửa dưới là phần khởi tạo VM.
Bài kiểm tra bốn màu
Để biết kernel treo thật hay chỉ là màn hình không cập nhật, tôi boot một bản shim đặc biệt: sau 5 giây nó lần lượt thử chuyển màn hình sang xanh lá → xanh dương → vàng → trắng bằng các cách khác nhau. Chỉ màu xanh lá hiện lên — tức là thao tác chuyển buffer qua VIG0 + CTL0 chạy đúng trên spes.
Vậy chỗ kẹt nằm ở logic khác. Đọc code thì thấy: driver framebuffer chỉ bật chế độ flip (double buffer + console shadow) khi kernel có RAM dưới 4 GiB. Topaz chạy với --highmem — kernel chỉ có RAM trên 4 GiB — nên chưa bao giờ đi qua đoạn đó. Spes không có bank cao kiểu topaz; tôi đã đặt kernel ở 0xb0000000 và kích hoạt đúng đoạn code chưa ai test.
19:11 — Cho kernel dùng 1 GiB ở 0x1_00000000 để né đoạn flip. Lần này chữ chạy tới:
cannot mount root, errno = 19
Đây là điểm dừng mong đợi: UFS đọc được GPT, tìm thấy userdata, nhưng trong đó vẫn là ext4/f2fs của Android. Toàn bộ phần cứng lõi của spes đã chạy được XNU.
Bản đồ RAM cuối cùng
1 GiB là quá ít cho iOS. Sau vài lần thử, cấu hình cho spes là:
- ADT
--midmem: kernel quản lý RAM từ0x80000000, và boot-argsweetfb_flip=0để tắt đường flip (patch 0952 thêm log chẩn đoán, 0954 cho panic vẫn hiện được khi tắt flip). - Log
SWEET INTF1: 319 frames in 5 sxác nhận panel chạy ~64 Hz và kernel không hề treo — chỉ là console bị vẽ vào một buffer mà panel không hiển thị.
4. Đêm 1/11: dựng root filesystem trên CI
Root iOS là một volume HFS+ 16 GB, dựng từ ba dmg trong IPSW iPhone X 16.7.16 (OS, Cryptex1,SystemOS, Cryptex1,AppOS). Vấn đề: máy Mac của tôi lúc đó chỉ còn 4,3 GB trống, và không có sudo không mật khẩu.
Giải pháp là đẩy hết sang GitHub Actions (workflow iosroot.yml, runner Ubuntu):
- Đọc
BuildManifest.plistqua remotezip, chọn đúng identityd22apErase. apfs-fusemount các dmg APFS,mkiosroot.shchép vào một image HFS+ 16 GB,sweet-root-tweaks.pyáp các chỉnh sửa (launchd plist, migration, plugin âm thanh…).img2simg→simg2simgchia thành các mảnh sparse ~1 GB.- Mã hoá từng mảnh bằng AES-256-CTR với khoá trong repo secret
ROOT_KEY(IV là số thứ tự mảnh). Repo public thì ai đăng nhập GitHub cũng tải được artifact — nội dung của Apple không được phép lộ ra dạng đọc được.
Trên máy local, script flash-root.sh tải từng mảnh, giải mã, kiểm tra SHA-256 rồi fastboot flash userdata — mỗi lúc chỉ cần ~2 GB trống.
00:35 ngày 2/11 — Tôi gõ “flash đi”. Android trên máy chính thức bị xoá.
5. Sáng 2/11: màn hình khoá, và cảm ứng không chạy
08:30 — Logo Apple, rồi màn hình khoá iOS 16 trên Redmi Note 11. Nút nguồn, tăng/giảm âm lượng hoạt động ngay (PM6125 PON và GPIO5, giống topaz). Nhưng chạm không có phản ứng.
Settings → General → About, chụp chiều 2/11 sau khi cảm ứng chạy. iOS tin rằng nó đang ở trên một chiếc iPhone X (node /product mượn từ device tree D22AP); “Capacity 17 GB” chính là volume HFS+ 16 GiB trong userdata.
Vấn đề: không đọc được log
Cảm ứng bật lên thì màn hình đen sau logo. Lý do ban đầu hoá ra do chính tôi: boot-arg chẩn đoán sweettouchdiag=2 cố ý panic sau 30 giây, và với sweetfb_flip=0 panic không hiện lên. Nhưng bài học lớn hơn là: đọc log bằng cách chụp màn hình điện thoại không bền. Chữ trôi, ảnh bị cắt mép, và mỗi vòng thử tốn vài phút trao đổi.
Topaz có sẵn sweetlog: kernel ghi console vào một ring 4 MiB ở cuối userdata, flush mỗi N giây. Còn thiếu cách đọc nó ra. OrangeFox Recovery cho spes là boot image v3 và không chạy được bằng fastboot boot trên ABL này. Repack lại thành header v4 bằng mkbootimg, thêm một segment cpio chứa /adb_keys (khoá adb của Mac) vào ramdisk — và nó chạy, adb vào được ngay.
Từ đó mỗi vòng thử thành một script:
boot kernel thử → (dùng máy, rồi giữ Nguồn + Giảm âm lượng)
→ boot OrangeFox → dd 1025 block cuối của userdata → giải mã ring → file log
Cảm ứng: ba lớp sai khác
Đối chiếu device tree từ mã nguồn kernel spes (kernel_xiaomi_sm6225):
- Chip: FocalTech FT3518 (chip-id
54:52) trên QUP0 SE2 (0x4a88000), IRQ GPIO80, reset GPIO86. Driver topaz chấp nhận chip-id cụ thể của topaz → nới ra cho mọi chip FocalTech hợp lệ (patch 0953). - Nguồn AVDD là GPIO98, nằm trên tile SOUTH của TLMM (
0x900000), trong khi topaz dùng GPIO36 ở tile EAST. Thêm boot-argsweettouch_avdd=98. - Đã cấp đúng GPIO mà I2C vẫn NACK. Đọc lại thanh ghi CTL của GPIO98 thì thấy bit 11 (
EGPIO_PRESENT) bật nhưng bit 12 (EGPIO_ENABLE) tắt. Trên khaje/bengal, chân nào có eGPIO thì phải set bit 12 thì TLMM mới thực sự lái chân đó — Linux làm việc này trongmsm_pinctrl, driver của topaz thì không cần vì GPIO36 không có eGPIO. Một dòng code:
if (cfg & 0x800u) { /* EGPIO_PRESENT */
cfg |= 0x1000u; /* EGPIO_ENABLE */
}
Sau patch 0956: probe 0x38 err=0/0 chip-id=54:52, selected Focaltech 54:52. Vuốt được.
Còn một chi tiết nhỏ nhưng làm vuốt lên không mở khoá được: topaz scale toạ độ thô ×4/5, còn FT3518 trên spes báo thẳng toạ độ pixel. Vuốt từ đáy màn hình bị co về 80% chiều cao, không còn là “vuốt từ mép”. Boot-arg sweettouch_div=4 → toạ độ 1:1, mở khoá bình thường.
6. Chiều 2/11: “giật quá” và bài toán GPU
Lên được home screen, mở được Settings, nhưng giật. Câu hỏi đầu tiên của tôi là “có phải do Metal chưa port đúng?” — không phải. Metal chưa hề được port, kể cả trên topaz. Lúc đó iOS đang vẽ hoàn toàn bằng CPU:
- CoreAnimation dùng renderer phần mềm
CA::OGL::SWvì không thấy GPU nào. - Mỗi frame 1080×2400 (~10 MB) được CPU chép ra panel.
Thêm nhân CPU cũng không cứu được: 4 nhân A53 không hỗ trợ trang 16K mà userland iOS cần, nên XNU chỉ chạy được trên 4 nhân A73 (cpus=8 vẫn ra selected=4).
Con đường duy nhất là bật GPU theo cách của topaz/HackiOS:
flowchart LR
CA["CoreAnimation<br/>renderer OpenGL ES"] --> EAGL["EAGLContext<br/>7 method bị thay<br/>(libtopaz-gl)"]
EAGL --> MESA["Mesa 25.0.7<br/>freedreno · OSMesa ES 3.2"]
MESA --> DRM["redwood-drm<br/>giả msm DRM ioctl"]
DRM --> RG["RedwoodGPU<br/>(IOKit user client)"]
RG --> AD["TopazAdreno<br/>zap shader · SMMU pass-through · CP ring"]
AD --> GPU["Adreno 610"]
Có bốn mảnh phải ráp: kernel driver, thư viện userland, cách nạp thư viện vào backboardd, và cách đưa file vào root.
Mảnh 1: kernel — và vùng nhớ GPU đè lên kernel
Kernel đã có sẵn driver Adreno, chỉ cần gpu=1. Lần boot đầu (t7): TopazGPU: Adreno 610 ready, RedwoodGPU published — rồi panic vài giây sau.
Nguyên nhân nằm trong thiết kế của topaz: trên topaz, kernel chỉ dùng bank RAM cao; bank giữa (0x80000000–0x13fffffff) không ai dùng, nên topaz lấy 1 GB cố định ở 0x90000000 làm pool riêng cho GPU (GPU không với tới trên 4 GiB qua SMMU). Trên spes, vùng đó chính là RAM của kernel. GPU và kernel cùng ghi vào một chỗ.
Patch 0957 biến pool thành boot-arg (gpu_pool=0x80000000 gpu_pool_mb=512), và ADT dời kernel lên 0xA0000000 + 2.5 GiB:
0x40000000 ─ 0x80000000 carve-out của Qualcomm (modem, TZ…)
0x80000000 ─ 0xA0000000 pool GPU 512 MB (kernel không quản lý)
0xA0000000 ─ 0x140000000 RAM của XNU, 2.5 GiB
Mảnh 2: build Mesa cho iOS mà không có iOS SDK
Máy Mac không cài Xcode đầy đủ. Lại CI: workflow gpu-userland.yml chạy trên runner macos-15:
mk-mesa-mac.shbuild Mesa 25.0.7 (freedreno + softpipe, OSMesa với profile OpenGL ES) bằng clang của Xcode, targetarm64-apple-ios16.0.eagl.ccủa topaz#include "eagl_gen.h"— một file sinh tự động mà repo không có. Generatormkeagl.pyvà bảngeagl_slots.txt(409 entry của bảng hàm sau-[EAGLContext getMacroContextPrivate], đọc ra từ OpenGLES.framework) nằm trong repo HackiOS (MIT). Vendor hai file đó vào, kèm credit.- Header IOKit không có trong iPhoneOS SDK → symlink header của macOS SDK (cùng interface).
Kết quả: libOSMesa.8.dylib (15 MB), libtopaz-gl.dylib, libtopaz-inject.dylib. Build lần đầu mất chưa tới 4 phút.
Mảnh 3: nạp vào backboardd
launchd bỏ qua biến DYLD_* trong job description, nên topaz có patch kernel kern.exec_inject: boot-arg topaz_inject=/topaz/lib/libtopaz-inject.dylib,backboardd khiến kernel tự thêm DYLD_INSERT_LIBRARIES khi exec đúng tên chương trình. Thư viện inject đợi service RedwoodGPU xuất hiện rồi mới dlopen thư viện GL.
Mảnh 4: chép 3 file vào HFS+ mà không có HFS+ driver
OrangeFox (Linux) không có hfsplus. Thử biến OrangeFox thành USB mass storage qua configfs — init của recovery reset cấu hình USB mỗi khi UDC rebind, và lần thứ hai thì máy kẹt luôn ở enumerate.
Cách cuối cùng chậm nhưng chắc:
adb exec-out ddkéo nguyên 16 GiB root về Mac (17 phút, ~16 MB/s).cp -c— clone APFS, không tốn thêm dung lượng.hdiutil attachbản clone, macOS đọc/ghi HFS+ native, chép 3 dylib vào/topaz/lib.- So sánh hai image theo từng 1 MiB: chỉ 25 chunk thay đổi. Ghi đúng 25 MB đó ngược xuống máy qua
adb exec-in, đọc lại từng chunk để xác minh.
7. Panic 0x3af64000 — một bug 32-bit trong XNU
Boot t9 với GPU + Mesa. Log rất hứa hẹn:
redwood-gl: 7 of 7 methods of EAGLContext are ours; OpenGL ES by Mesa, off-screen (OSMesa)
redwood-gl: an OpenGL ES 3 context for CoreAnimation: OpenGL ES 3.2 Mesa 25.0.7; FD610
Mesa đã nói chuyện được với Adreno thật. Rồi panic:
panic(cpu 2 caller 0xfffffe0007b8aa08): phystokv_range: illegal PA: 0x3af64000;
phys base 0xa0000000, size 0xa0000000 @arm_vm_init.c:406
Màn hình panic chỉ còn đuôi, không có backtrace — chừng này là đủ để lần ra bug.
Kernel không có KASLR trong cấu hình này, atos cho ngay phystokv_range.cold.1. Nhưng ai gọi nó với một địa chỉ vật lý thấp hơn cả đầu DRAM (0x40000000)?
Lọc tất cả lời gọi phystokv_range trong XNU: bcopy_phys, bzero_phys, dcache_incoherent_io_* đều có kiểm tra pmap_valid_address / isphysmem và dùng copy window cho RAM không thuộc kernel. Còn lại flush_dcache(…, phys=TRUE), và một hàm trong pmap.c:
void
pmap_sync_page_attributes_phys(ppnum_t pp)
{
flush_dcache((vm_offset_t) (pp << PAGE_SHIFT), PAGE_SIZE, TRUE);
}
ppnum_t là 32 bit. Phép dịch xảy ra trước khi ép kiểu, nên với trang ở 0x13af64000 (phần RAM trên 4 GiB của spes, pp = 0x4ebd9):
0x4ebd9 << 14 = 0x13af64000 (toán học)
= 0x3af64000 (trong 32 bit)
Đúng con số trên màn hình. Hàm này chạy khi một trang đổi cache attribute — chính là lúc Mesa map buffer GPU ở chế độ write-combined. Trên spes, RAM của kernel vắt qua mốc 4 GiB (0xA0000000–0x140000000), nên chỉ cần một trang rơi vào phần trên 4 GiB là địa chỉ bị cắt mất bit cao.
Patch 0959:
pmap_paddr_t pa = ptoa(pp); /* ép kiểu trước, dịch sau */
if (pa_valid(pa)) { /* pool GPU không thuộc kernel: không có gì để flush */
flush_dcache((vm_offset_t) pa, PAGE_SIZE, TRUE);
}
Trong lúc truy bug này tôi cũng thêm patch 0958 để SavePanicInfo tự ghi panic và backtrace xuống sweetlog — trên spes gPanicBase không được khởi tạo nên panic log mặc định không đi đâu cả. (Thú thật là bug được tìm ra bằng cách đọc code trước khi patch đó kịp có ích.)
8. Tối 2/11: GPU chạy, và tối ưu những frame chậm
19:27 — Bản t12 boot lên, giao diện được vẽ bằng Adreno 610. “Đỡ giật và mượt hơn rồi, nhưng chưa mượt hẳn.”
Settings lúc 19:43 ngày 2/11, khi CoreAnimation đã vẽ bằng Adreno 610 qua Mesa freedreno thay cho renderer phần mềm.
libtopaz-gl có profiler tích hợp, log ra mỗi 20 frame. Một đoạn điển hình:
redwood-gl: profile: 11326 us a frame in GL calls, 8798 outside them;
most time: glBindTexture, glFinishObjectAPPLE, glTexImage2D, glFlush
Khoảng 20 ms/frame, tức tối đa ~50 fps, xen kẽ những frame 130–170 ms. Hai việc tiếp theo:
Tăng xung GPU từ 300 lên 600 MHz (gpu_mhz=600). Mức 600 lấy từ GPLL0 nên an toàn; các mức 785–1260 MHz cần vote điện áp GX qua RPM mà kernel chưa làm, có thể treo máy.
Frame 114 ms khi mở Settings. Log t13 nói rất rõ:
RedwoodGPU: surface 159 (1125x2436, 11239424 bytes) not shared:
phys 0x117d38000, contiguous 16384 bytes
redwood-gl: frame 25: … 1 picture copied out … 114348 us (99684 copying out)
Surface cỡ màn hình của app không liền mạch về vật lý, nên GPU (SMMU pass-through, địa chỉ GPU = địa chỉ vật lý) không dùng chung được. eagl phải glReadPixels cả khung về CPU: 100 ms chỉ để chép. Topaz đã có cơ chế giữ sẵn 10 buffer liền khối 12 MB cho việc này (patch 0949), nhưng chỉ xin khi surface đầu tiên được tạo. Trên spes, tới lúc đó RAM đã vụn:
SWEET SURF: 0 contiguous buffers of 12 MB set aside for screen-sized surfaces
Patch 0961 dời việc xin buffer về lúc IOCoreSurfaceRoot khởi động, khi RAM của kernel còn nguyên một khối. Đây là thứ tôi đang thử ở thời điểm viết bài.
9. Danh sách patch cho spes
| Patch | Nội dung |
|---|---|
| 0952 | Chẩn đoán đường flip của framebuffer, sweetfb_flip=0 |
| 0953 | Cảm ứng: AVDD GPIO98 (tile SOUTH), mọi chip FocalTech, sweettouch_div |
| 0954 | Panic hiện được trên màn hình khi tắt flip |
| 0955 | Log trạng thái LDO PM6125 (SPMI) và đọc lại GPIO cảm ứng |
| 0956 | EGPIO_ENABLE cho chân có eGPIO |
| 0957 | Pool GPU cấu hình bằng gpu_pool / gpu_pool_mb |
| 0958 | Ghi panic + backtrace vào sweetlog |
| 0959 | Sửa tràn 32-bit trong pmap_sync_page_attributes_phys |
| 0960 | sweetsurftrace=1: log từng bước tạo IOSurface |
| 0961 | Giữ buffer màn hình ngay lúc khởi động |
Cộng thêm hai workflow CI (iosroot.yml, gpu-userland.yml) và một ADT riêng cho bản đồ RAM của spes.
Boot-args hiện tại:
cpus=4 rd=ufsroot0 ufsrw=1 rootrw=1 sweetfb_flip=0 sweetfb_noconsole=1
sweettouch=1 sweettouch_avdd=98 sweettouch_div=4 sweetkeys=1 sweetlog=1
gpu=1 gpu_midmem=1 gpu_pool=0x80000000 gpu_pool_mb=512 gpu_mhz=600
topaz_inject=/topaz/lib/libtopaz-inject.dylib,backboardd
10. Những gì chưa xong
- Pin và sạc: spes dùng
sc8551+bms, khác topaz; hiện đang chạy pin giả. - Đường flip của framebuffer vẫn làm treo hiển thị khi kernel có RAM dưới 4 GiB.
- Một lần treo chưa giải thích được (t11) ngay lúc
backboarddtạo surface đầu tiên — bản có log từng bước thì không tái hiện, gợi ý một race. - Render thẳng vào surface hiển thị thay vì qua OSMesa off-screen, và xung GPU cao hơn 600 MHz (cần vote GX qua RPM).
- Wi-Fi, modem, âm thanh: topaz đã có, chưa thử trên spes.
- Vulkan/Turnip — nhánh riêng, đang dở.
11. Vài điều rút ra
Cùng SoC không có nghĩa là cùng máy. CPU, GIC, UFS, MDP chạy ngay từ lần đầu. Thứ tốn thời gian là những giả định ngầm: kernel luôn có RAM trên 4 GiB, GPIO cảm ứng luôn không có eGPIO, pool GPU luôn là vùng không ai dùng. Mỗi giả định đúng trên topaz, sai trên spes, và mỗi cái lộ ra dưới dạng một triệu chứng chẳng liên quan (màn hình đứng, NACK I2C, panic địa chỉ lạ).
Đầu tư vào khả năng quan sát trước khi đoán. Bước ngoặt không phải patch nào cả, mà là lúc có vòng lặp boot → OrangeFox → dd → log tự động. Trước đó mỗi lỗi tốn nhiều vòng chụp ảnh và đoán; sau đó mỗi vòng thử còn khoảng 10 phút và log đủ để đọc.
Bug cũ của upstream sống lâu nhất ở chỗ không ai chạm tới. (pp << PAGE_SHIFT) với ppnum_t 32 bit đã nằm trong XNU từ rất lâu. Nó không gây hại trên phần cứng Apple, và chỉ lộ ra khi một chiếc Redmi giá rẻ có RAM vắt qua mốc 4 GiB chạy Mesa với write-combine.
Làm việc với AI agent ở tầng này khác hẳn viết web. Agent không nhìn thấy màn hình điện thoại; tôi không đọc nổi 900 patch trong một đêm. Cách chia việc hiệu quả nhất hoá ra là: agent giữ toàn bộ ngữ cảnh code + log và đề xuất thí nghiệm có kết quả phân biệt được (bài kiểm tra bốn màu là ví dụ hay nhất), còn con người làm tay chân, cầm máy, và giữ quyền quyết định những bước không quay lại được như xoá Android. Những lúc nó sai (bật nhầm boot-arg cố ý panic, thử biến recovery thành ổ USB làm kẹt máy) thì log và ảnh chụp kéo nó về lại rất nhanh.
Credits
- MakrSas/ios-topaz — nền tảng toàn bộ dự án: shim, patch XNU, driver SM6225.
- HackiOS (OOSEX GANG) — RedwoodGPU, eagl, các patch Mesa cho Darwin,
mkeagl.py,kern.exec_inject(APSL 2.0 / MIT). - Jabiyeff/kernel_xiaomi_sm6225 — device tree của spes để đối chiếu cảm ứng.
- Mesa freedreno, OrangeFox Recovery, apfs-fuse.
- XNU của Apple (APSL 2.0). iOS và mọi thành phần userland thuộc về Apple; bài viết và repo không phân phối lại chúng.
Mục đích bài viết
Bài viết và mã nguồn đi kèm chỉ nhằm mục đích nghiên cứu và học tập: tìm hiểu cách một kernel (XNU), bootloader (ABL), device tree và driver phần cứng (UFS, MDP/DSI, I2C, TLMM, SMMU, Adreno) phối hợp với nhau, và ghi lại quá trình gỡ lỗi ở tầng thấp.
- Đây không phải sản phẩm, không phải ROM để dùng hằng ngày, và không có hướng dẫn cài đặt cho người dùng cuối. Máy chạy như trong bài vẫn thiếu pin/sạc, mạng di động, camera và nhiều thứ khác.
- Tôi không phân phối IPSW, root filesystem, dyld shared cache hay bất kỳ binary nào của Apple. Các mảnh root dựng trên CI đều được mã hoá và tự hết hạn sau vài ngày.
- Dự án không liên kết và không được bảo trợ bởi Apple, Xiaomi hay Qualcomm. iOS, iPhone là nhãn hiệu của Apple Inc.; Redmi là nhãn hiệu của Xiaomi.
- Việc mở khoá bootloader, ghi đè
userdatavà chạy kernel tự build có thể làm mất dữ liệu, mất bảo hành hoặc làm hỏng thiết bị. Nếu bạn tự thử, hãy làm trên máy không còn dùng tới và tự chịu trách nhiệm. - Hãy tôn trọng giấy phép của các dự án được dùng: XNU (APSL 2.0), ios-topaz, HackiOS (APSL 2.0 / MIT), Mesa (MIT) và điều khoản sử dụng phần mềm của Apple.