Port it to your phone
On a Snapdragon phone, almost everything that is specific to the Redmi Note 10 Pro sits in three places: the device tree generator, the boot shim, and the board drivers in the kernel patches. This page walks through all three and shows where to look up your phone's values.
Before you touch anything, back up your phone. Boot a recovery such as OrangeFox or TWRP and pull at least persist, modemst1, modemst2, fsg, fsc, boot, dtbo and vbmeta. Some of these hold calibration data that is unique to your phone and can't be downloaded anywhere. Keep them out of git.
Check the compatibility page first. This guide assumes a Snapdragon chip with UFS storage and a bootloader that supports fastboot boot.
Step 0: get your phone's hardware map
Everything you need to know about the hardware is already on the phone, in the device tree Android booted with. Boot a recovery with adb (or use a rooted Android) and copy it to the PC:
adb shell cat /sys/firmware/fdt > live.dtb
adb shell cat /proc/iomem > iomem.txt
dtc -I dtb -O dts -o live.dts live.dtb # dtc is in the device-tree-compiler package
live.dts is a big text file. Every value below comes from searching it. You'll also want your phone's kernel source (Xiaomi, OnePlus and others publish it on GitHub) for anything the device tree doesn't spell out, like the touch controller's protocol.
Also unpack your stock boot.img, because the shim reuses its DTB:
python3 tools/unpack_bootv2.py backup/boot.img dtb/boot_unpacked
1. The device tree generator: tools/mkadt.py
XNU learns about the hardware from an Apple Device Tree. mkadt.py writes one from the constants at the top of the file. These are the ones to change:
| Constant | What it is | Where to find it in live.dts |
|---|---|---|
HIGH_PHYS_BASE, HIGH_MEM_SIZE | The RAM the kernel may use | The memory node, plus every entry under reserved-memory. Pick a range with no carve-outs in it. On sweet that's the whole bank above 4 GB. |
GICD, GICR | Interrupt controller | interrupt-controller@... with compatible = "arm,gic-v3". The first two reg entries. |
FB, FB_SIZE | The screen buffer the bootloader leaves on | cont_splash_region (sometimes splash_region) under reserved-memory |
FB_W, FB_H, FB_STRIDE | Panel resolution and bytes per line | The panel node (qcom,mdss_dsi_...), qcom,mdss-dsi-panel-width and -height. Stride is usually width x 4. |
UFS, UFS_SIZE | The storage controller | ufshc@..., the reg named ufs_mem |
TIMEBASE, VTIMER_INTID | System timer | The timer node with arm,armv8-timer. The frequency is almost always 19.2 MHz on Snapdragon. The virtual timer is the third interrupt; a PPI number n becomes INTID 16 + n. |
| CPU nodes | How many cores, and their hardware IDs | The cpus node: each core's reg (its MPIDR). sweet has 8 cores at 0x000 to 0x700; the loop in mkadt.py builds them as core << 8. |
2. The boot shim: shim/
The shim is the small program the bootloader starts. It has its own copy of a few addresses, because it runs before any device tree is read:
shim/console.handshim/mmu.h: the framebuffer address (FB_BASE,FB_PHYS).shim/shim.c:GICD_BASE,GICR_BASE, the timer interrupts,PSHOLD(the reset register) andIMEM_RESTART_REASON(used to reboot into fastboot). Search live.dts forrestart@/qcom,psholdandrestart_reason.shim/build.sh: themkbootimgoffsets and header version. Copy them from your stock boot.img: the unpack script prints its header with the load addresses and page size.
Two kernel patches also draw on the screen very early, before the device tree is parsed: 0010-sweet-early-framebuffer-debug and 0012-sweet-keep-debug-console-after-ttbr0. They hard-code the framebuffer address and the 1080 x 2400 size. Change those too, or the early boot messages will land in the wrong place.
3. The board drivers: xnu/patches/
The kernel is Apple's source plus a numbered series of patches that GitHub Actions applies in order. Most patches are generic. These are the ones that know about the board:
| Patch | What to check |
|---|---|
0060-sweet-keys-touch-drivers | Buttons: the PMIC bus (SPMI) addresses and the register offsets of the power key (PON) and volume keys. Touch: which I²C serial engine the touch chip hangs off, its I²C address, and its reset and interrupt GPIOs. Search live.dts for your touch chip's node (goodix, focaltech, novatek, synaptics, ...). |
0062-sweet-touch-power-battery | The power rail the touch chip needs (on sweet, regulator ldoc11 voted through RPMh). Your phone's touch node names its supply, like vdd-supply or avdd-name. |
0063-sweet-goodix-geni-packing | The Goodix GT9896 touch protocol. A different touch chip needs a different protocol here. The Linux driver for your chip in the vendor kernel source shows the register layout. |
0061-sweet-iphone-x-display-size | Centers the real panel inside the 1125 x 2436 screen that iOS sees, and maps touch coordinates. Uses 1080 x 2400 in a few places. |
0072 to 0077 (USB) | Optional. The USB controller addresses for the live log over USB. You can skip these at first and read the log from recovery. |
The patches add new files to XNU, such as bsd/dev/sweet_touch.c. To change one, apply the series to an xnu-8796 tree, edit the file, and regenerate that patch with diff -u. Before you spend a CI run, tools/xnu-cc.py can compile a single file locally using the commands from a previous CI log.
4. Boot arguments
The command line is baked into the payload by tools/mkpayload.py (see the README for the full line). For a first boot on a new phone, use cpus=1, and add sweettouch=0 sweetkeys=0 until those drivers are adapted. Raise cpus once the kernel is stable.
5. Build and boot
From here the steps are the same as on sweet and are in the README: build the kernel on GitHub Actions, build the iOS root image from the iPhone X IPSW, pack the boot image, flash the root to userdata, then fastboot boot the shim.
Take it in stages. You can test the shim and kernel before you flash anything: boot the shim alone, and it prints its own diagnostics to the screen.
- Shim text on screen. Your framebuffer address is right.
- Kernel messages scroll. Memory, interrupts and timer are right. If the phone reboots instantly, the memory range probably overlaps a protected carve-out.
- The UFS driver finds the disk. Then iOS starts from
userdata. This is the point where you flash the root image. - Apple logo, then the lock screen (after several minutes). Touch and buttons come last.
When something crashes, read the log from recovery with tools/sweet-log-pull.sh, and tools/sweet-crash.py turns crash reports into function names. NOTES.md has every problem sweet ran into along the way, and most of them will show up on your phone too.
Got further on your phone? Post it on r/hackios. Which chip, which step you reached, and what you had to change helps everyone after you.