Background
Awhile ago, I bought a Xiaomi Redmi Note 11 (the Chinese version - 21091116AC). Which was not what I had in mind I’ll be honest with you, as the listing showed a Snapdragon version of it, and not the Mediatek based model I got.
But fine, it wasn’t worth the hassle trying to return it, and it did what I needed it to, so I kept it.
However, since these are pretty old it came with firmware V14.0.2.0.TGBINXN (keep that INXN in mind, it’ll be relevant), meaning the security patches were from late 2023, not ideal, even if my main use case for this device is a glorified hotspot.
The beginning
So I did as one does, and headed over to XM Firmware Updater to find the latest firmware available for this device. The latest version I found was the new HyperOS firmware - OS1.0.1.0.TGBCNXM (China) or OS1.0.1.0.TGBINXM (India).
Now, since my device is the Chinese variant I (foolishly) assumed that I should download the TGBCNXM version and flash that.
But as the firmware it came with weirdly was named TGBINXN and not TGBINXM like I thought it should be, I figured it was prudent to make sure to have a backup of all partitions before I start toying with it.
Backup
This led me to a nifty tool called mtkclient, which allows you to read/write and erase partitions (and potentially unlock the bootloader as well), without having to modify the software on the device.
I’ll assume Linux here, but the mtkclient documentation explains how to use it on MacOS and Windows as well.
Install the latest version from GitHub with pipx (if you haven’t used it before, run pipx ensurepath
to get mtkclient in your path)
1 | pipx install git+https://github.com/bkerler/mtkclient.git |
Now, power off your device if you haven’t already (and keep the USB cable disconnected), and start mtkclient (there’s also a GUI client mtk_gui
if you prefer using that):
1 | mtk rl ./evergo_backup/ --skip=userdata |
What this will do is read all partitions except userdata (but you can include that as well if you want to, I just want to save time and space) into evergo_backup.
To let mtk start reading we need to get our phone into BROM mode.
So with the device turned off, press both volume down/up and insert the USB cable. Then you should see mtk react and start reading in a few second.
If not, keep the volumes buttons pressed and hold the power button in as well until you see a reaction.
Now get yourself a cup of your favorite poison, and wait until it’s complete. Check the collapsed section for which files you should be seeing.
List of dumped partitions
1 | boot_a.bin |
Should you need to restore your backup, to flash everything back do:
1 | mtk wl ./evergo_dump/ |
First attempts at flashing
As my goal was to eventually get over to a custom ROM, I decided I’d be best of using the latest version of MIUI (and not HyperOS), I downloaded V14.0.4.0.TGBINXM, rebooted my phone to the bootloader and tried to run the included flash_all.sh
script.
When complete it automatically rebooted, but sadly nothing’s ever easy and after it attempted to boot a few times, I got the dreaded “NV data is corrupted” error.
So I recovered from my backup like shown above, and decided I’d try to flash a custom ROM directly instead.
I chose a ROM to test, flashing it’s boot partition (which includes recovery), ran fastboot reboot recovery
, and tried to flash the ROM via ADB sideload.
But that too would not work:
[ERROR:delta_performer.cc(747)] Unable to initialize partition metadata for slot B
[ERROR:download_action.cc(227)] Error ErrorCode::kInstallDeviceOpenError (7) in DeltaPerformer’s Write method when processing the received payload – Terminating processing
Investigating the stock ROM
So I decided I needed to figure out what on earth was wrong with this stock ROM.
Seeing as the ROM it came with seemed like a global/Indian one, despite the device being the Chinese variant, I had ChatGPT write me a couple scripts to compare my firmware dump to the downloaded V14.0.2.0 for with China and India.
Script to copy partititon A files
1 |
|
Script to compare the partitions (Python)
1 | #!/usr/bin/env python3 |
Using those scripts, I could quickly compare 40+ partitions between the stock firmware and the downloaded V14.0.2.0:
1 | ./copy_part_a.sh ./evergo-dump/ ./cmp/ |
Warning: The comparision script loads the whole file into memory, so if you have super.bin in there, it’ll load 16GB+ into memory, which will quickly starve your system if you don’t have enough.
Comparison results with Indian version of V14.0.2.0
1 | Comparing ../Downloads/evergo_in_global_images_V14.0.2.0.TGBINXM_13.0/images/pi_img.img to cmp/pi_img.img... |
From the results, you can see most of the ROM is the same as the official Indian version of the ROM,
with some key differences.
Despite all that, I was never really ever able to find out why I got NV data corruption problems after flashing the official ROM.
I saw someone that likely has the same weird version of the device on the Telegram group for this phone, which had some success by flashing someone else’s backup of their NV data.
So I figured I’d try the same, and flashed that on my device together with the official ROM for India.
And that did end up getting my device to boot with the official ROM, but it didn’t detect the SIM card anymore, and baseband version was unknown and no IMEI’s showing. With custom ROM’s showing the same behaviour, something was clearly wrong.
While I can’t be sure, my assumption was that it could have something to do with their being a mismatch with what the NV data was saying my IMEI’s was, and what my modem actually has in reality.
So I tried to Modem Meta, SN write tool and TFT unlock tool to see if I could correct the NV data I flashed to the correct IMEI - but all to no avail (they wouldn’t recognize the phone).
Give up, or?
With so many failures, and too many hours spent, I was close to giving up. But I realized there was one last thing I could attempt before throwing in the towel - GSI’s.
Generic System Image (GSI) ROM’s - are generic ROM’s that can run on a plethora of devices. Since these require less modifications to the stock ROM, I figured maybe that could be an option for me.
So I went to the Project Treble GSI list and picked out a ROM. I don’t have a strong preference for any of these, but I’ve tested LinageOS (A15) by MisterZtr, and Project Matrixx (A14) by ChonDoit.
After downloading and decompressing the system image, I rebooted my phone to the bootloader (adb reboot bootloader
), and then into fastbootd (userspace mode): fastboot reboot fastboot
.
This is because the bootloader doesn’t have access to the inner logical partitions in the super partition (e.g. system), which we need access to.
Now in fastbootd, let’s flash our GSI:
1 | fastboot flash system LineageOS-22.1-20250113-VANILLA-EXT4-GSI.img |
Sadly though, this would not work either:
1 | Resizing 'system_a' FAILED (remote: 'Not enough space to resize partition') |
Now that’s weird, why doesn’t it fit? Let’s check the partition size:
1 | fastboot getvar all 2>&1 | grep partition-size:system_a |
Converted to decimal that’s 1394757632 bytes, so 1.39 GB. Now how big was the LinageOS image again?
1 | ls -l LineageOS-22.1-20250113-VANILLA-EXT4-GSI.img |
Right, that’s a whole lot more (2.57 GB), no wonder it won’t work.
So what I tried at first was to manually resize the partition using fastboot resize-logical-partition
, but that wouldn’t work either. Then I realized the product partition was immense, around 4.4 GB, and with everything else, there probably just wasn’t enough space for a larger system partition.
What can we do then? Well as it turns out, the product partition is used alongside the system partition (see here for details). And as you might guess, our GSI ROM probably won’t need any of that.
First attempt was trying to just delete the product_a partition and flash the system image after:
1 | fastboot delete-logical-partition system_a |
But no, no such luck this time too. After faffing about with this for awhile, I remembered having toyed with the Android partition tools trying to figure out why custom ROM’s wouldn’t flash on the stock ROM.
In that case, could I build my own super.img to flash with everything needed in it?
And as in turns out, the answer is yes.
I got the necessary tools for this with the android-tools
package on Arch, but you should be able to find it for your distribution as well.
Firstly, let’s unpack the super.bin we dumped earlier to get each individual partition:
1 | mkdir ./evergo-gsi |
Then we need to convert each file to a sparse image:
1 | img2simg ./mi_ext_a.bin ./mi_ext_a.img |
Then we can create the super image with lpmake:
1 | lpmake \ |
I’ve tried to keep it as close to stock, and just lower the size of product_a. I’m not sure if it matters, but I decided to keep it defined, and just not include the image for it.
If you need to compare the super image to your stock dump, remove the --sparse
argument, so you’ll be able to use lpdump on it to compare.
If everything ran successfully, it’s now time to flash our super image!
Note that I’ve included a format of userdata here (as you’ll have to), so make sure to have a backup of any data you care about.
The oem cdms
command isn’t very well documented, but it seems to stop dm-verity issues from happening.
1 | fastboot flash super ./super.img |
Now, hopefully your phone should restart into LinageOS, or whatever GSI ROM you chose.
If it loops and ends up in recovery, what worked for me was to factory reset the phone there and then reboot again.
With a little luck, you shouldn’t have any issues now :)
Do note though, that Android 15 GSI’s currently have issues with the fingerprint readers, so if you need that stick to A13 or A14.
At least on Project Matrixx I haven’t encountered any issues yet, and I hope it’ll stay that way.
If you got this far, thanks for coming along on this crazy and frustrating journey with me :)