{"uuid": "635163ba-6362-40b0-9feb-4f9581c05400", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2018-6242", "type": "seen", "source": "https://gist.github.com/4Sitam4/69efb144cc4a0375d2d7466149960f0f", "content": "================================================================================\n NEXUS 7 2012 (grouper) \u2014 COMPLETE UNBRICK GUIDE\n From \"APX only, black screen, no bootloader\" to a fully booting Android 5.1.1\n WITHOUT blob.bin, WITHOUT the SBK, WITHOUT a second tablet.\n\n Status: SOLVED. Device boots Android 5.1.1 (LMY47V), all partitions mounted.\n Written 2026-08-22 after a full recovery session.\n================================================================================\n\n\n                          ### READ THIS FIRST ###\n\nIf you followed tofurky/tegra30_debrick, got the Google logo back, and are now\nstuck with:\n\n    mmcblk0: mmc0:0001 HBG4e 29.1 GiB\n    mmcblk0: unknown partition table\n    fs_mgr: Failed to mount ... by-name/APP at /system : No such file or dir\n\n...then jump straight to SECTION 3. The answer is a 4 MiB address-space shift\nbetween nvflash and the Linux kernel that is documented nowhere in the debrick\nrepo. It cost me an entire day. It will cost you five minutes.\n\n\n                        ### SCOPE AND WARNINGS ###\n\nWHAT THIS DOES: rebuilds the partition table, the BCT, the bootloader and a\nstock Android 5.1.1 on a Nexus 7 2012 whose bootloader is gone. IT ERASES\nEVERYTHING. There is no data-recovery path here \u2014 if the tablet only boots to\nAPX, its contents were already unreachable.\n\nWHAT IT CANNOT FIX: a physically worn-out eMMC. That is the one genuine death\nfor this model. You will know because writes fail with \"partition not found in\npartition table\". Everything I wrote read back byte-identical, so mine was\nfine.\n\nRISK: low, and reversible. The Fus\u00e9e Gel\u00e9e vulnerability lives in mask ROM, so\nno failed flash can close the door. I destroyed my own BCT twice by accident\nand recovered both times with a single --create. If a step fails, power-cycle\nand try again.\n\nTIME: about 40 minutes of actual work if you follow this in order \u2014 2.5 min per\n--create, a few seconds for the GPT, then 7-10 min for the first Android boot.\nIt took me two days to find out what to type.\n\nWHO THIS IS FOR: you are comfortable with a Linux shell, `dd`-level concepts,\nand reading hex. If you are not, the risk is not bricking the tablet further \u2014\nit is already bricked \u2014 but wasting hours.\n\n\n================================================================================\n TABLE OF CONTENTS\n================================================================================\n\n 0.  TL;DR \u2014 the three things that actually matter\n 1.  Device identity and how to read it\n 2.  Background: why this brick is recoverable at all\n 3.  ** THE KEY DISCOVERY: the eMMC boot-partition offset **\n 4.  Prerequisites (hardware, host OS, packages, files)\n 5.  Host-side USB tuning (do this or nothing will work)\n 6.  Getting into APX mode\n 7.  STEP-BY-STEP RECOVERY PROCEDURE\n     7.1  Blank the BCT bootloader slot\n     7.2  Read the real partition table\n     7.3  Build a correct flash.cfg\n     7.4  Expand the sparse factory images\n     7.5  Run --create\n     7.6  Build and write the GPT\n     7.7  Boot\n     7.8  Adapting this to an 8 GB or 16 GB grouper\n 8.  Optional: enabling adb from the ramdisk (for debugging)\n 9.  Every trap, with the exact error message\n10.  Reference data for grouper (partition tables, offsets, checksums)\n11.  Complete scripts\n12.  Disproven hypotheses \u2014 do not waste time on these\n13.  Full chronology of the session\n14.  Remaining work / notes\n15.  Sources\n\n\n================================================================================\n 0. TL;DR \u2014 THE THREE THINGS THAT ACTUALLY MATTER\n================================================================================\n\n(1) THE BCT SHIPPED IN tegra30_debrick HAS A BOOTLOADER ALREADY DECLARED.\n    `nvflash --create` fails with \"BCT is full; no more bootloaders can be\n    added\". Zero the counter at offset 3920 and blank the 44-byte entry at\n    3924. Then --create inserts YOUR bootloader and recomputes its AES hash\n    with YOUR device's hardware key. This is what un-bricks the device.\n\n(2) `--download EBT` DOES NOT WRITE THE BCT, AND `--setbct --configfile` WILL\n    DESTROY A GOOD ONE. Everything must go through a single `--create`, and\n    nothing may touch the BCT afterwards.\n\n(3) nvflash AND THE KERNEL DO NOT AGREE ON SECTOR ZERO.\n    NVIDIA's bootloader linearises eMMC boot0 -&gt; boot1 -&gt; user area. The kernel\n    only sees the user area. The offset is 8192 sectors of 512 B (4 MiB):\n\n        kernel_LBA = nvflash_LBA - 8192\n\n    `nvflash --create` never writes a GPT. You must write it yourself, at the\n    physical location the bootloader advertises, but with every LBA inside it\n    expressed in KERNEL space. Get this wrong and the kernel silently reports\n    \"unknown partition table\".\n\n\n================================================================================\n 1. DEVICE IDENTITY AND HOW TO READ IT\n================================================================================\n\nDevice       : Asus/Google Nexus 7 (2012) Wi-Fi\nCodename     : grouper (build target \"nakasi\")\nSoC          : NVIDIA Tegra 3 (T30), rev A03, SKU 0x83\nRAM          : 1 GB\nStorage      : 32 GB eMMC, \"HBG4e\"\nBootloader   : 4.23\nLast OS      : Android 5.1.1, build LMY47V\n\nRead the chip with the patched nvflash after the exploit:\n\n    chip id            : 0x30  major: 1  minor: 3\n    chip sku           : 0x83\n    chip uid           : 0x0000000000000000015d2ea4d1600215\n    sbk burned         : true\n    dk burned          : true\n    boot device        : emmc\n    operating mode     : 3          &lt;- forced non-secure by the payload\n    sdram config strap : 1\n\nFrom the running kernel:\n\n    ext_csd.sectors 0x3a40000  =  61,079,552 sectors of 512 B\n                               =  31,272,730,624 bytes  =  29.1 GiB\n    mmcblk0boot0 / mmcblk0boot1 : 2.00 MiB each\n\nFrom fastboot:\n\n    version-bootloader : 4.23      version-hardware : ER3\n    variant / product  : grouper   secure  : no      unlocked : yes\n\nNOTE ON CAPACITY: `ext_csd.sectors` is the USER AREA only. The two 2 MiB eMMC\nboot partitions are NOT counted in it, but the bootloader addresses them as if\nthey came first. This is the whole subject of section 3.\n\n\n================================================================================\n 2. BACKGROUND: WHY THIS BRICK IS RECOVERABLE AT ALL\n================================================================================\n\nThe folklore says a Nexus 7 2012 with a wiped bootloader and no prior blob.bin\nbackup is dead, because the Tegra 3 Secure Boot Key (SBK) is fused per-device\nand classic nvflash refuses to talk without it.\n\nThat was true until Fus\u00e9e Gel\u00e9e (CVE-2018-6242). The Tegra bootrom's USB\nrecovery stack has a stack-overflow that lets you run arbitrary code before any\nauthentication. tofurky/tegra30_debrick ports it to Tegra 3. The payload\noverrides getSecurityMode() to always return 3 (production non-secure), so the\nbootrom accepts an unsigned RCM message.\n\nTwo consequences worth stating plainly:\n\n  - You never need to KNOW the SBK. The nvflash miniloader runs ON the Tegra\n    and uses the hardware AES engine, which can use the fused key without\n    revealing it. It recomputes the bootloader hash for you.\n\n  - The vulnerability is in mask ROM. No failed flash can ever close it. The\n    device is permanently recoverable.\n\nThe XDA thread titled \"requires another Nexus 7 2012\" describes a DIFFERENT\nmethod where the second tablet was an AES compute engine \u2014 and it still needed\nthe bricked unit's SBK. Ignore it. You do not need a second device.\n\n\n================================================================================\n 3. ** THE KEY DISCOVERY: THE eMMC BOOT-PARTITION OFFSET **\n================================================================================\n\nThis is the part that is missing from every guide I could find.\n\nAn eMMC exposes three independent address spaces: boot0, boot1, and the user\narea. Linux surfaces them as /dev/block/mmcblk0boot0, mmcblk0boot1, mmcblk0.\n\nNVIDIA's bootloader \u2014 and therefore nvflash \u2014 does NOT see them that way. From\nthe upstream Tegra Partition Table patch series by Dmitry Osipenko:\n\n    \"eMMC storage has two special boot partitions in addition to the main one.\n     NVIDIA's bootloader linearizes eMMC boot0-&gt;boot1-&gt;main accesses, this\n     means that the partition table addresses are shifted by the size of boot\n     partitions.\"\n\nSo nvflash sees ONE flat device:\n\n    nvflash LBA 0          = eMMC boot0, sector 0\n    nvflash LBA 4096       = eMMC boot1, sector 0\n    nvflash LBA 8192       = user area, sector 0   = kernel mmcblk0 LBA 0\n\n    boot_offset = 2 boot partitions x 2 MiB = 4,194,304 B = 8192 sectors of 512\n\n    ==&gt; kernel_LBA = nvflash_LBA - 8192\n\nCONSEQUENCES, all of which bit me:\n\n a) The BCT and PT live in the eMMC BOOT partitions, not at the start of the\n    user area. `nvflash --rawdeviceread 0 1` returns BCT data, and it is\n    genuine \u2014 but that sector is boot0, and the kernel cannot see it.\n\n b) An MBR written at nvflash LBA 0 lands in boot0. The kernel reads mmcblk0\n    LBA 0 and finds nothing. My MBR attempt failed for this reason alone.\n\n c) The `gpt_sector=` value the bootloader passes to the kernel is already in\n    KERNEL space. The documented Tegra formula is:\n\n        gpt_sector = ext_csd.sectors_num - ext_csd.boot_sectors_num - 1\n\n    For this device:  61,079,552 - 8,192 - 1 = 61,071,359\n\n    To write there with nvflash you must add the offset back:\n\n        nvflash LBA = 61,071,359 + 8,192 = 61,079,551\n        byte offset = 61,079,551 x 512   = 31,272,730,112\n        4096-byte sector = 31,272,730,112 / 4096 = 7,634,943  (partial)\n\n d) Every LBA INSIDE the GPT \u2014 my_lba, partition_entry_lba, last_usable_lba,\n    and every partition's starting/ending LBA \u2014 must be in KERNEL space, i.e.\n    nvflash offsets minus 4,194,304 bytes.\n\n e) The EBT (bootloader) partition starts at nvflash byte 3,670,016, which is\n    still inside the 4 MiB boot area. It is therefore partly invisible to the\n    kernel. Do not put it in the GPT. Nothing in Android needs it there.\n\nI spent hours writing structurally perfect GPTs \u2014 parted validated them, CRCs\nrecomputed, header at the exact advertised sector \u2014 and the kernel rejected all\nof them, because `is_gpt_valid()` checks `my_lba == lba` and my my_lba was in\nnvflash space. One number.\n\n\n================================================================================\n 4. PREREQUISITES\n================================================================================\n\nHARDWARE\n  - The bricked Nexus 7 2012.\n  - A micro-USB cable that carries DATA. Many are charge-only. Test it on a\n    phone first. This is the single most common cause of failure.\n  - Ideally an Intel USB controller. I did the whole recovery on AMD (Ryzen 7\n    7840HS, xHCI) with zero failures once section 5 was applied, so the\n    \"Intel required\" folklore is overstated \u2014 but tune USB first.\n  - Battery: mine read 100 % / 4.164 V and was never a factor. The bootloader\n    does refuse to write below ~29 %, so charge it if in doubt. The symptom is\n    a misleading `bootloader failed NvError 0x0`.\n\nHOST OS AND PACKAGES\n  Any Linux with a 32-bit loader. I used CachyOS (Arch). nvflash is a 32-bit\n  i386 ELF, which is the only unusual requirement.\n\n  ARCH / CACHYOS  (enable [multilib] in /etc/pacman.conf first)\n\n    sudo pacman -S --needed \\\n        lib32-glibc lib32-gcc-libs \\\n        android-tools python-pyusb \\\n        usbutils e2fsprogs parted \\\n        git unzip curl cpio gzip diffutils\n\n  DEBIAN / UBUNTU\n\n    sudo dpkg --add-architecture i386 &amp;&amp; sudo apt update\n    sudo apt install -y \\\n        libc6:i386 libstdc++6:i386 \\\n        android-sdk-platform-tools python3-usb \\\n        usbutils e2fsprogs parted \\\n        git unzip curl cpio gzip diffutils\n    # older releases: use `fastboot adb` instead of android-sdk-platform-tools\n\n  FEDORA\n\n    sudo dnf install glibc.i686 libstdc++.i686 \\\n        android-tools python3-pyusb usbutils e2fsprogs parted \\\n        git unzip curl cpio gzip diffutils\n\n  NIXOS\n    nvflash cannot run directly (it looks for /lib/ld-linux.so.2, which does\n    not exist). The error is a misleading \"No such file or directory\" on a file\n    that is clearly there. Wrap it:\n\n        nix-shell -p steam-run python3Packages.pyusb android-tools usbutils\n        sudo -E steam-run ./utils/nvflash_..._miniloader_patched ...\n\n    The -E is mandatory, otherwise sudo drops the nix-shell environment and\n    python3 can no longer find pyusb.\n\n  WHAT EACH ONE IS FOR\n    lib32-glibc, lib32-gcc-libs   run the 32-bit nvflash binary\n    python3 + pyusb               fusee-launcher.py (the exploit)\n    android-tools                 adb, fastboot, simg2img, mkbootimg,\n                                  unpack_bootimg \u2014 all four are used\n    usbutils                      lsusb; the ONLY reliable way to know what\n                                  mode the tablet is in (see section 6)\n    e2fsprogs                     debugfs, to inspect system-raw.img without\n                                  mounting it or needing root\n    parted                        optional, to validate a hand-built GPT on\n                                  the host before writing it to the device\n    git unzip curl                fetching the repo and the factory image\n    cpio gzip                     only if you patch the ramdisk (section 8)\n    diffutils                     cmp, to verify GPT read-back\n\n  NOT NEEDED, despite what other guides say\n    cbootimage / bct_dump \u2014 only useful to BUILD a BCT from source. We patch\n    a prebuilt one with a few bytes of Python instead. On Arch it is AUR-only,\n    so skipping it saves a build.\n\n  VERSIONS THAT WORKED HERE\n    Python 3.14.7, pyusb 1.3.1     \u2014 no compatibility issues with the 2018\n                                     fusee-launcher.py, despite its age\n    android-tools 37.0.0           \u2014 but see 7.4: this fastboot cannot flash\n                                     the sparse factory images to this\n                                     bootloader. Flash raw images instead.\n    nvflash v1.13.87205            \u2014 the miniloader-patched one from the repo\n\n  CHECK EVERYTHING IS PRESENT\n\n    for t in lsusb adb fastboot simg2img mkbootimg unpack_bootimg \\\n             debugfs parted cmp cpio gzip python3; do\n        printf '%-16s ' \"$t\"; command -v $t || echo MISSING\n    done\n    python3 -c 'import usb.core; print(\"pyusb OK\")'\n    ls -l /lib/ld-linux.so.2 &amp;&amp; echo \"32-bit loader OK\"\n    ./tegra30_debrick/utils/nvflash_v1.13.87205_miniloader_patched --help \\\n        &gt;/dev/null &amp;&amp; echo \"nvflash runs OK\"\n\n  PERMISSIONS AFTER UNZIPPING\n    If you got the repo as a ZIP rather than via git, Unix permissions are\n    lost and nothing will start:\n\n        chmod +x tegra30_debrick/fusee-launcher/fusee-launcher.py\n        chmod +x tegra30_debrick/utils/nvflash_v1.13.87205*\n\nFILES\n  - git clone --recursive https://github.com/tofurky/tegra30_debrick\n  - Factory image nakasi-lmy47v-factory-5a0bb059.zip from\n    https://developers.google.com/android/images#nakasi\n\n  Verified checksums (sha256):\n    bootloader-grouper-4.23.img  2f57520cacc4cba48708a5a0937daec9\n                                 0ab15e03b856a9a9244312f1e42afc2c\n    boot.img                     c4e28e713517289be6e8dee4a5cdd384\n                                 5f2c4280e3425cf871ebe0823e83ce2a\n    recovery.img                 17934bbc258f17cb3ec824a2154e88e7\n                                 02eb806eca9571e9eb86bfe0600cfb3c\n    system.img (sparse)          5a31745b54bab058e6ce45cbd0ecaf71\n                                 3087542a11911c571928035da902ae56\n    userdata.img (sparse)        a5c8989d4dc42d63f53678cb1ecad10a\n                                 2fa2496e7d7e494d374ee5a1e9dd80fc\n\n  The bootloader image in tegra30_debrick is BIT-IDENTICAL to the factory one.\n  The postmarketOS wiki warning that Google's grouper bootloader is \"broken\"\n  does not hold up.\n\nUDEV (so you can work without sudo)\n  /etc/udev/rules.d/51-nexus7.rules :\n\n    SUBSYSTEM==\"usb\", ATTR{idVendor}==\"0955\", MODE=\"0666\", TAG+=\"uaccess\"\n    SUBSYSTEM==\"usb\", ATTR{idVendor}==\"18d1\", MODE=\"0666\", TAG+=\"uaccess\"\n    SUBSYSTEM==\"usb\", ATTR{idVendor}==\"0b05\", MODE=\"0666\", TAG+=\"uaccess\"\n    ACTION==\"add\", SUBSYSTEM==\"usb\", ATTR{idVendor}==\"0955\", \\\n        TEST==\"power/control\", ATTR{power/control}=\"on\"\n\n    udevadm control --reload-rules &amp;&amp; udevadm trigger --subsystem-match=usb\n\n\n================================================================================\n 5. HOST-SIDE USB TUNING \u2014 DO THIS FIRST\n================================================================================\n\nMy tablet would not enumerate at all:\n\n    usb 1-1: new high-speed USB device number 7 using xhci_hcd\n    usb 1-1: device descriptor read/64, error -110      (timeout)\n    usb 1-1: device descriptor read/64, error -71       (protocol error)\n    usb usb1-port1: attempt power cycle\n    usb 1-1: device not accepting address 9, error -62\n    usb usb1-port1: unable to enumerate USB device\n    xhci_hcd 0000:06:00.3: Timeout while waiting for setup device command\n\nTwo runtime knobs fixed it completely and permanently:\n\n    echo Y     | sudo tee /sys/module/usbcore/parameters/old_scheme_first\n    echo 30000 | sudo tee /sys/module/usbcore/parameters/initial_descriptor_timeout\n\n`old_scheme_first` is the important one. The kernel has two enumeration\nschemes; the old one RESETS the device before reading its descriptors instead\nof after. Slow or marginally-powered devices only survive that one.\n\nAlso kill USB autosuspend:\n\n    for f in /sys/bus/usb/devices/*/power/control; do echo on | sudo tee $f; done\n\nThese are lost on reboot of the HOST. Re-apply them.\n\nIf you see the enumeration failures ONLY after the bootloader is partly\nworking, note that a half-initialised Tegra USB stack produces exactly the same\nerrors. It is not necessarily your cable.\n\n\n================================================================================\n 6. GETTING INTO APX MODE\n================================================================================\n\nAPX (a.k.a. RCM) is the Tegra hardware recovery mode. It enumerates as\n`0955:7330 NVIDIA Corp. APX`.\n\nTHE SCREEN TELLS YOU NOTHING. In APX the ROM does not touch the display at\nall \u2014 it neither lights it nor clears it, so the panel keeps whatever image was\nlast drawn. A frozen \"Google + padlock\" splash during APX is normal. Only lsusb\nis authoritative. I wasted real time on this.\n\nFROM A DEAD DEVICE (physical):\n    1. Unplug the USB cable.\n    2. Hold Power ~10 s to force a full power-off (clears RAM).\n    3. Press and HOLD Volume Up.\n    4. Plug the USB cable in while still holding Volume Up, and keep holding\n       for a few more seconds.\n  The ROM samples the buttons at the instant power arrives. Pressing after\n  plugging is too late.\n\nFROM A DEVICE THAT BOOTS ENOUGH TO RUN adbd (huge time-saver):\n    adb reboot forced-recovery      -&gt; goes straight to APX, no buttons\n    adb reboot bootloader           -&gt; goes to fastboot\n\nCRITICAL: THE BOOTROM ACCEPTS EXACTLY ONE RCM MESSAGE PER POWER-ON.\nEvery failed exploit attempt burns the session. Retrying without a full power\ncycle always fails with `usb.core.USBTimeoutError: [Errno 110]`. Automate the\n\"wait for disappear, then reappear\" cycle; do not hammer it.\n\nSECOND CRITICAL GOTCHA: after a SUCCESSFUL exploit the Tegra re-enumerates\nunder a NEW USB device number. If nvflash starts too early it grabs the dying\nhandle and dies with:\n\n    usb read error (108): Cannot send after transport endpoint shutdown\n    uid read failed\n\nWait for the device NUMBER to change, not merely for the node to be writable.\nA fixed `sleep 2` is not reliable.\n\n\n================================================================================\n 7. STEP-BY-STEP RECOVERY PROCEDURE\n================================================================================\n\nSet BASE to your working directory. Everything below assumes:\n\n    BASE=~/nexus7-recovery\n    DEB=$BASE/tegra30_debrick\n    NVF=$DEB/utils/nvflash_v1.13.87205_miniloader_patched\n    FL=$DEB/fusee-launcher/fusee-launcher.py\n    PAYLOAD=$DEB/payload/uart_payload_n7.bin\n\n\n--------------------------------------------------------------------------------\n 7.1  BLANK THE BCT BOOTLOADER SLOT\n--------------------------------------------------------------------------------\n\nbct/nexus_7_grouper_bct.bin already declares one bootloader \u2014 the dumper's,\nwith THEIR AES hash, which can never match your SBK. `--create` refuses to add\na second and fails with:\n\n    failed executing command 17 NvError 0x120002\n    command failure: create failed (bad data)\n    bootloader status: BCT is full; no more bootloaders can be added (code: 16)\n\nLayout of the relevant region (offsets in the 6128-byte file):\n\n    3920 : BootLoadersUsed        = 1\n    3924 : BootLoader[0].Version  = 1\n    3928 : StartBlock             = 224\n    3932 : StartPage              = 0\n    3936 : Length                 = 2150992\n    3940 : LoadAddress            = 0x80108000\n    3944 : EntryPoint             = 0x80108000\n    3948 : Attributes             = 4\n    3952 : Bl AES Hash            = b28ebc06accf2bcd877e444bc28d00c0\n\nFix:\n\n    python3 - &lt;&lt;'EOF'\n    import struct, pathlib\n    src = pathlib.Path('tegra30_debrick/bct/nexus_7_grouper_bct.bin')\n    d = bytearray(src.read_bytes())\n    struct.pack_into(' 0x00080000).\n\nSizing arithmetic for a 32 GB grouper:\n\n    device                     31,272,730,624\n    all partitions except UDA   1,192,230,912\n    UDA                        30,080,499,712   &lt;- device - others\n                              ----------------\n    total                      31,272,730,624   exact fit\n\nThe full literal file is reproduced in section 11.1.\n\nNote `type=GPT` on partition 16 \u2014 the repo uses that too. It makes nvflash\nALLOCATE the region but, as I discovered the hard way, nvflash NEVER WRITES ANY\nGPT CONTENT INTO IT. Reading the partition back after --create gives 524,288\nbytes of zeros. You will write it yourself in 7.6.\n\n\n--------------------------------------------------------------------------------\n 7.4  EXPAND THE SPARSE FACTORY IMAGES\n--------------------------------------------------------------------------------\n\nsystem.img and userdata.img start with magic 0x3aff26ed \u2014 Android sparse\nformat. nvflash writes files byte-for-byte and does not understand it.\n\n    simg2img nexus7-factory/nakasi-lmy47v/image/system.img system-raw.img\n    # -&gt; exactly 681,574,400 bytes, matching the APP partition to the byte\n\nboot.img and recovery.img start with \"ANDROID!\" and are already raw; use them\nas-is.\n\nModern fastboot (37.x) cannot flash the sparse images either \u2014 this 2012\nbootloader rejects the current sparse protocol:\n\n    Sending 'cache' (424 KB)   OKAY\n    Writing 'cache'            FAILED (remote: '(NotSupported)')\n\n`fastboot getvar max-download-size` returns EMPTY here, which is probably why.\nFlashing RAW images over fastboot works fine.\n\n\n--------------------------------------------------------------------------------\n 7.5  RUN --create\n--------------------------------------------------------------------------------\n\nEnter APX. Then, in one go:\n\n    python3 $FL $PAYLOAD -P 7330            # expect \"Launch complete!\"\n    # WAIT for the USB device number to change, then:\n\n    $NVF --bct bct-blank.bin --setbct \\\n         --configfile flash-grouper-real.cfg \\\n         --create \\\n         --bl $DEB/bootloader/bootloader-grouper-4.23.img\n    #    ^^^ NOTE: no --go, so the nv3pserver stays alive for step 7.6\n\nExpected output:\n\n    setting device: 2 3\n    deleting device partitions\n    creating partition: BCT\n    creating partition: PT\n    creating partition: EBT\n    creating partition: SOS\n    creating partition: LNX\n    creating partition: APP\n    creating partition: CAC\n    creating partition: MSC\n    creating partition: USP\n    creating partition: PER\n    creating partition: MDA\n    creating partition: UDA\n    creating partition: GPT\n    ... sending file: ... sent successfully  (x4)\n\nTakes about 2.5 minutes, mostly the 650 MB system image.\n\n`--create` is the ONLY nvflash operation that writes a valid BCT (\"syncing bct\nat end\"). At this point the device will already cold-boot to the bootloader\nsplash. That is real progress \u2014 but Android still cannot boot, because there is\nno GPT yet.\n\n\n--------------------------------------------------------------------------------\n 7.6  BUILD AND WRITE THE GPT   ** THE STEP NO GUIDE MENTIONS **\n--------------------------------------------------------------------------------\n\nConvert every nvflash offset to kernel space by subtracting 4,194,304 bytes\n(8192 sectors of 512). EBT falls below zero \u2014 it lives inside the eMMC boot\narea \u2014 so omit it. Android never needs it in the GPT.\n\n    part   nvflash byte     kernel byte     kernel LBA start .. end\n    SOS       7,864,320       3,670,016          7,168 ..     31,743\n    LNX      20,447,232      16,252,928         31,744 ..     48,127\n    APP      28,835,840      24,641,536         48,128 ..  1,379,327\n    CAC     710,410,240     706,215,936      1,379,328 ..  2,286,591\n    MSC   1,174,929,408   1,170,735,104      2,286,592 ..  2,287,615\n    USP   1,175,453,696   1,171,259,392      2,287,616 ..  2,308,095\n    PER   1,185,939,456   1,181,745,152      2,308,096 ..  2,318,335\n    MDA   1,191,182,336   1,186,988,032      2,318,336 ..  2,319,359\n    UDA   1,191,706,624   1,187,512,320      2,319,360 .. 61,070,335\n\nHeader placement:\n\n    gpt_sector (kernel LBA)      = 61,071,359\n      = ext_csd.sectors - boot_sectors - 1\n      = 61,079,552 - 8,192 - 1\n    partition_entry_lba (kernel) = 61,071,327   (32 sectors before the header)\n    last_usable_lba (kernel)     = 61,071,326\n    first_usable_lba             = 34\n\n    write location, nvflash space:\n      header LBA        = 61,071,359 + 8,192 = 61,079,551\n      entries LBA       = 61,071,327 + 8,192 = 61,079,519\n      byte range        = 31,272,713,728 .. 31,272,730,624\n      4096-byte sectors = 7,634,939 .. 7,634,943  (5 sectors, 20,480 bytes)\n\nGenerator (writes gpt-kernel.bin) \u2014 see section 11.2 for the literal script.\n\n  Watch out: `buf[a:][:n] = x` in Python assigns to a COPY and silently does\n  nothing. Use `buf[a:a+n] = x`. I lost a cycle to that.\n\nWrite it in the session left open by 7.5, verify, then boot:\n\n    $NVF --resume --rawdevicewrite 7634939 5 gpt-kernel.bin\n    $NVF --resume --rawdeviceread  7634939 5 gpt-readback.bin\n    cmp gpt-kernel.bin gpt-readback.bin &amp;&amp; echo \"GPT verified\"\n    $NVF --resume --go\n\nALWAYS read back and compare. One of my earlier attempts wrote to the right\nplace but I never verified it, and I chased the wrong hypothesis for an hour.\n\n\n--------------------------------------------------------------------------------\n 7.8  ADAPTING THIS TO AN 8 GB OR 16 GB grouper\n--------------------------------------------------------------------------------\n\nEvery number printed in this document is for a 32 GB unit. Do NOT copy them\nblindly. Only three things are device-specific, and all three are discoverable.\n\nSTEP 1 \u2014 get your eMMC size.\n  From the kernel log of any boot that gets that far:\n\n      adb pull /proc/last_kmsg\n      grep -a ext_csd /proc/last_kmsg\n      -&gt; [mmc] mmc_read_ext_csd: ext_csd.sectors 0x3a40000 prod_name HBG4e\n\n  Or, if the device does not boot at all, from the eMMC datasheet / the\n  capacity printed on the box. Known values:\n\n      8 GB   ext_csd.sectors ~0x0e90000   ( 7,634,944 sectors)\n      16 GB  ext_csd.sectors ~0x1d20000   (30,539,776 sectors)\n      32 GB  ext_csd.sectors  0x3a40000   (61,079,552 sectors)   &lt;- mine\n\n  These are indicative. Read the real value if you can; a wrong figure puts\n  the GPT in the wrong place and you get \"unknown partition table\" again.\n\nSTEP 2 \u2014 get your real partition table.\n  Section 7.2, `--resume --getpartitiontable`. The small partitions (BCT, PT,\n  EBT, SOS, LNX, MSC, USP, PER, MDA) are the same on every grouper. APP and\n  CAC may differ between builds. UDA absorbs the rest.\n\n  IF YOU CANNOT READ IT (a destroyed PT will make the command fail), use the\n  32 GB table from section 10 and only recompute UDA:\n\n      UDA = eMMC_bytes - (sum of every other partition) \n\n  With the sizes from section 10 that sum is 1,192,230,912 bytes, so:\n\n      8 GB   UDA =  3,908,530,176 - 1,192,230,912 =  2,716,299,264\n      16 GB  UDA = 15,636,365,312 - 1,192,230,912 = 14,444,134,400\n      32 GB  UDA = 31,272,730,624 - 1,192,230,912 = 30,080,499,712\n\n  Put that in flash.cfg with allocation_attribute=8, NOT 0x808. See 7.3.\n\nSTEP 3 \u2014 let the script do the rest.\n  make-gpt.py (section 11.2) parses your flash.cfg, rebuilds nvflash's\n  sequential layout, subtracts the 8192-sector boot offset, computes\n  gpt_sector = ext_csd.sectors - 8192 - 1, and prints the exact\n  --rawdevicewrite command for your device:\n\n      ./make-gpt.py flash-grouper-real.cfg 0x1d20000     # 16 GB example\n\n  It refuses to run if the layout overshoots the device or if any partition\n  would extend past gpt_sector \u2014 the two mistakes I actually made.\n\nTHE BOOT OFFSET IS ALWAYS 8192 SECTORS on grouper: two eMMC boot partitions of\n2 MiB. Confirm with `adb shell cat /proc/partitions` \u2014 mmcblk0boot0 and\nmmcblk0boot1 should both read 2048 (KiB).\n\n\n--------------------------------------------------------------------------------\n 7.7  BOOT\n--------------------------------------------------------------------------------\n\nExpected sequence:\n\n    Google logo + unlocked padlock     bootloader splash\n    four spinning coloured dots        Android 5.1 boot animation\n    setup wizard                       done\n\nFirst boot takes 7-10 minutes because PackageManager runs dex2oat over every\nsystem APK into /data/dalvik-cache. Mine reached sys.boot_completed=1 at 416 s\nof uptime. Maps and GoogleCamera are the slow ones. Do not interrupt it.\n\nVerification from the host:\n\n    adb shell cat /proc/partitions\n\n    major minor  #blocks  name\n     179     0  30539776  mmcblk0\n     179     1     12288  mmcblk0p1     SOS\n     179     2      8192  mmcblk0p2     LNX\n     179     3    665600  mmcblk0p3     APP\n     179     4    453632  mmcblk0p4     CAC\n     179     5       512  mmcblk0p5     MSC\n     179     6     10240  mmcblk0p6     USP\n     179     7      5120  mmcblk0p7     PER\n     179     8       512  mmcblk0p8     MDA\n     179     9  29375488  mmcblk0p9     UDA\n     179    32      2048  mmcblk0boot1\n     179    16      2048  mmcblk0boot0\n\n    adb shell df\n\n    /system   639.8M  633.0M    6.8M\n    /cache    436.0M    7.3M  428.7M\n    /data      27.6G  675.3M   26.9G\n\n    adb shell getprop ro.build.display.id     -&gt; LMY47V\n\n\n================================================================================\n 8. OPTIONAL: ENABLING adb FROM THE RAMDISK (FOR DEBUGGING)\n================================================================================\n\nIf /system will not mount you have no /system/bin/sh and `adb shell` is dead.\nYou can still get a diagnostic channel by patching boot.img's ramdisk.\n\n    unpack_bootimg --boot_img boot.img --out out/\n    mkdir rd &amp;&amp; cd rd &amp;&amp; gzip -dc ../out/ramdisk | cpio -idm\n\nEdit default.prop:\n\n    ro.secure=0\n    ro.debuggable=1\n    ro.adb.secure=0                    &lt;- key: no on-screen RSA prompt\n    persist.sys.usb.config=mtp,adb\n\nRepack. The --owner flag is mandatory: cpio records the CURRENT owner, and init\nrefuses a ramdisk owned by a normal user.\n\n    find . | cpio -o -H newc --owner 0:0 | gzip -9 &gt; ../ramdisk-mod.gz\n    cd .. &amp;&amp; mkbootimg --kernel out/kernel --ramdisk ramdisk-mod.gz \\\n        --base 0x10000000 --kernel_offset 0x00008000 \\\n        --ramdisk_offset 0x01000000 --tags_offset 0x00000100 \\\n        --pagesize 2048 --header_version 0 --output boot-adb.img\n\nThe stock grouper boot.img has an EMPTY cmdline field \u2014 everything comes from\nthe bootloader \u2014 and these load addresses reproduce the original header\nexactly.\n\nWHAT WORKS WITHOUT A SHELL: `adb pull` needs no shell. /proc/partitions,\n/proc/mounts, /proc/version, /proc/last_kmsg, /proc/kallsyms, /proc/iomem and\n/default.prop all pull fine. /proc/cmdline and /dev are refused by SELinux \u2014\nand the sync protocol reports that as \"does not exist\", which is misleading.\n`adb ls ` works and is the easiest way to inspect /sys.\n\n/proc/last_kmsg holds the PREVIOUS boot's full kernel log, including the\ncommand line. That is how I obtained gpt_sector.\n\nWHAT DOES NOT WORK: dropping a static busybox at /system/bin/sh. The file is\nfound (\"Permission denied\" instead of \"No such file or directory\") but SELinux\nrefuses to execute it, and `androidboot.selinux=permissive` is compiled out of\n\"user\" builds. `dyndbg=\"file efi.c +p\"` reaches the kernel but produces nothing\nbecause CONFIG_DYNAMIC_DEBUG is off \u2014 all the GPT diagnostics are dead strings.\n\nREMEMBER TO RESTORE THE STOCK boot.img when you are done. The patched one is an\nunauthenticated root adb channel.\n\n    adb reboot bootloader\n    fastboot flash boot boot.img        # raw image, works fine\n    fastboot reboot\n\n\n================================================================================\n 9. EVERY TRAP, WITH THE EXACT ERROR MESSAGE\n================================================================================\n\n\"BCT is full; no more bootloaders can be added (code: 16)\"\n    The shipped BCT already declares a bootloader. See 7.1.\n\n\"setting device: 0 3 / specified device is invalid (code: 6)\"\n    Missing `type=sdmmc` in the [device] section. See 7.3.\n\n\"bootloader failed NvError 0x0\"\n    Battery under ~29 %. Charge for an hour. Not a hardware fault.\n\n\"partition not found in partition table\"\n    Genuinely a degraded eMMC \u2014 the one unrecoverable case for this model.\n    I never saw it; every write and read-back verified byte-identical.\n\n\"usb read error (108): Cannot send after transport endpoint shutdown\"\n\"uid read failed\"\n    nvflash grabbed the pre-exploit USB handle. Wait for the device NUMBER to\n    change after the exploit. See section 6.\n\n\"usb.core.USBTimeoutError: [Errno 110] Operation timed out\" on fusee-launcher\n    The RCM session is already consumed. Full power cycle required.\n\n\"Command send failed (usb write failed)\" with \"rcm version 0X4\"\n    You used the UNPATCHED nvflash on an SBK-burned part. Expected. Use the\n    miniloader-patched binary.\n\n\"Writing 'system' FAILED (remote: '(NotSupported)')\"\n    Modern fastboot's sparse protocol. Flash raw images instead. See 7.4.\n\n\"mmcblk0: unknown partition table\"\n    No valid GPT where the kernel looks. Section 3 and 7.6. Note the failure is\n    SILENT \u2014 all the informative messages in efi.c are pr_debug and\n    CONFIG_DYNAMIC_DEBUG is off on this build.\n\n\"exec '/system/bin/sh' failed: No such file or directory\"\n    /system is not mounted. Symptom, not cause \u2014 fix the partition table.\n\n\"adb: error: remote object '/dev/block/mmcblk0' does not exist\"\n    SELinux denying adbd access to /dev. The node exists. Misleading error.\n\nSilent BCT destruction (no error at all)\n    `--setbct --configfile ... --go` re-syncs the BCT to eMMC despite the help\n    text claiming it only loads to IRAM. Running it after a good --create\n    replaces your valid BCT with the repo's, and the device drops back to APX\n    on the next cold boot. I did this TWICE.\n\n    RULE: one --create does everything. Nothing touches the BCT afterwards.\n    If you need a live session for raw reads/writes, run --create WITHOUT --go\n    and chain --resume commands.\n\n`--sync` exists but does nothing useful\n    It is absent from the numbered `--help` list but present in the binary's\n    string table and accepted on the command line. The README tells you to add\n    it to the EBT command to rewrite the BCT. I verified by reading the BCT\n    partition back: it does not.\n\nPython slice-assignment trap\n    `buf[a:][:n] = x` modifies a temporary copy. Use `buf[a:a+n] = x`.\n\n\n================================================================================\n10. REFERENCE DATA FOR grouper (32 GB)\n================================================================================\n\neMMC geometry\n    ext_csd.sectors        0x3a40000 = 61,079,552 sectors of 512 B\n    user area              31,272,730,624 bytes (29.1 GiB)\n    boot0, boot1           2 MiB each -&gt; boot_offset 8192 sectors / 4,194,304 B\n    logical_block_size     512\n    physical_block_size    512\n\nAddress-space conversion\n    kernel_LBA  = nvflash_LBA  - 8192\n    kernel_byte = nvflash_byte - 4,194,304\n\nGPT placement\n    gpt_sector (kernel LBA)   61,071,359\n    header, nvflash LBA       61,079,551\n    entries, nvflash LBA      61,079,519\n    write at 4096-sector      7,634,939 (5 sectors, 20,480 bytes)\n\nnvflash partition table (as written by the working flash.cfg)\n    name  id   startSector  numSectors            bytes  fs\n    BCT    2             0         768        3,145,728  basic\n    PT     3           768         128          524,288  basic\n    EBT    4           896        1024        4,194,304  basic\n    SOS    5          1920        3072       12,582,912  basic\n    LNX    6          4992        2048        8,388,608  basic\n    APP    9          7040      166400      681,574,400  basic\n    CAC   10        173440      113408      464,519,168  ext3\n    MSC   11        286848         128          524,288  ext3\n    USP   12        286976        2560       10,485,760  ext3\n    PER   13        289536        1280        5,242,880  basic\n    MDA   14        290816         128          524,288  basic\n    UDA   15        290944     7344896   30,080,499,712  ext3\n    GPT   16       7634816         128          524,288  GPT\n\nResulting Linux partitions\n    mmcblk0p1  SOS   12,288 KiB     mmcblk0p6  USP   10,240 KiB\n    mmcblk0p2  LNX    8,192 KiB     mmcblk0p7  PER    5,120 KiB\n    mmcblk0p3  APP  665,600 KiB     mmcblk0p8  MDA      512 KiB\n    mmcblk0p4  CAC  453,632 KiB     mmcblk0p9  UDA 29,375,488 KiB\n    mmcblk0p5  MSC      512 KiB\n\nfstab.grouper (stock, unmodified \u2014 works once the GPT is right)\n    by-name/APP  /system    ext4  ro                        wait\n    by-name/CAC  /cache     ext4  noatime,...,errors=panic  wait,check\n    by-name/UDA  /data      ext4  noatime,...,errors=panic  wait,check,\n                                    encryptable=by-name/MDA\n    by-name/MSC  /misc      emmc  defaults\n    by-name/LNX  /boot      emmc  defaults\n    by-name/SOS  /recovery  emmc  defaults\n    by-name/USP  /staging   emmc  defaults\n  Only APP, CAC and UDA are actually mounted at boot.\n\nKernel command line supplied by the bootloader (for reference)\n    tegra_wdt.heartbeat=30 tegraid=30.1.3.0.0 mem=1022M@2048M\n    android.commchip=0 vmalloc=512M androidboot.serialno=\n    video=tegrafb no_console_suspend=1 console=none debug_uartport=hsport\n    usbcore.old_scheme_first=1 lp0_vec=8192@0xbddf9000\n    tegra_fbmem=8195200@0xabe01000 core_edp_mv=0 audio_codec=rt5640\n    board_info=f41:a00:1:44:2 tegraboot=sdmmc gpt gpt_sector=61071359\n    androidboot.bootloader=4.23 androidboot.baseband=unknown\n\nKernel\n    Linux 3.1.10-gf5d7b8b, \"Machine: grouper\"\n    Contains efi_partition, is_gpt_valid, read_lba, msdos_partition.\n    NO proprietary Tegra partition parser \u2014 it depends entirely on the GPT.\n\nis_gpt_valid() checks, in order (all must pass)\n    1  signature == \"EFI PART\"\n    2  header_size &lt;= logical_block_size (512)\n    3  header CRC32 (computed with the crc field zeroed)\n    4  my_lba == the LBA it was read from        &lt;- the one that caught me\n    5  first_usable_lba &lt;= last_lba\n    6  last_usable_lba  &lt;= last_lba\n    7  sizeof_partition_entry == 128\n    8  partition-entry-array CRC32\n\nfind_valid_gpt() passes force_gpt_sector as the READ LOCATION:\n    if (!good_agpt &amp;&amp; force_gpt &amp;&amp; force_gpt_sector)\n        good_agpt = is_gpt_valid(state, force_gpt_sector, &amp;agpt, &amp;aptes);\n\n\n================================================================================\n11. COMPLETE SCRIPTS\n================================================================================\n\n--------------------------------------------------------------------------------\n 11.1  flash-grouper-real.cfg (literal, replace )\n--------------------------------------------------------------------------------\n\n[device]\ntype=sdmmc\ninstance=3\n\n[partition]\nname=BCT\nid=2\ntype=boot_config_table\nallocation_policy=sequential\nfilesystem_type=basic\nsize=3145728\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n[partition]\nname=PT\nid=3\ntype=partition_table\nallocation_policy=sequential\nfilesystem_type=basic\nsize=524288\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n[partition]\nname=EBT\nid=4\ntype=bootloader\nallocation_policy=sequential\nfilesystem_type=basic\nsize=4194304\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\nfilename=/tegra30_debrick/bootloader/bootloader-grouper-4.23.img\n\n[partition]\nname=SOS\nid=5\ntype=data\nallocation_policy=sequential\nfilesystem_type=basic\nsize=12582912\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\nfilename=/nexus7-factory/nakasi-lmy47v/image/recovery.img\n\n[partition]\nname=LNX\nid=6\ntype=data\nallocation_policy=sequential\nfilesystem_type=basic\nsize=8388608\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\nfilename=/nexus7-factory/nakasi-lmy47v/image/boot.img\n\n[partition]\nname=APP\nid=9\ntype=data\nallocation_policy=sequential\nfilesystem_type=basic\nsize=681574400\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\nfilename=/system-raw.img\n\n[partition]\nname=CAC\nid=10\ntype=data\nallocation_policy=sequential\nfilesystem_type=ext3\nsize=464519168\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n[partition]\nname=MSC\nid=11\ntype=data\nallocation_policy=sequential\nfilesystem_type=ext3\nsize=524288\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n[partition]\nname=USP\nid=12\ntype=data\nallocation_policy=sequential\nfilesystem_type=ext3\nsize=10485760\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n[partition]\nname=PER\nid=13\ntype=data\nallocation_policy=sequential\nfilesystem_type=basic\nsize=5242880\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n[partition]\nname=MDA\nid=14\ntype=data\nallocation_policy=sequential\nfilesystem_type=basic\nsize=524288\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n[partition]\nname=UDA\nid=15\ntype=data\nallocation_policy=sequential\nfilesystem_type=ext3\nsize=30080499712\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n[partition]\nname=GPT\nid=16\ntype=GPT\nallocation_policy=sequential\nfilesystem_type=basic\nsize=524288\nfile_system_attribute=0\npartition_attribute=0\nallocation_attribute=8\npercent_reserved=0\n\n\n--------------------------------------------------------------------------------\n 11.2  make-gpt.py \u2014 derives everything from your flash.cfg\n--------------------------------------------------------------------------------\n\nWorks for any grouper capacity. Verified: run against the config in 11.1 it\nreproduces, byte for byte, the GPT that actually booted my device.\n\n    ./make-gpt.py flash-grouper-real.cfg 0x3a40000\n\n#!/usr/bin/env python3\n# Builds the GPT for a Nexus 7 2012 (grouper) of ANY capacity.\n#\n# Everything is derived from two inputs:\n#   - the flash.cfg you used with `nvflash --create` (partition order + sizes)\n#   - the eMMC size, i.e. ext_csd.sectors from the kernel log\n#\n# The only hand-entered constant is the eMMC boot-partition offset, which is\n# 8192 sectors (2 x 2 MiB) on every grouper.\n#\n# WHY THIS IS NEEDED: nvflash addresses the eMMC as one linear space\n# boot0 -&gt; boot1 -&gt; user area, while the kernel only sees the user area.\n# Every LBA inside the GPT must be in KERNEL space, but the GPT itself is\n# written at a NVFLASH sector. Mixing the two is why the kernel reports\n# \"unknown partition table\".\n#\n#   usage: ./make-gpt.py flash-grouper-real.cfg [ext_csd_sectors]\n#          ./make-gpt.py flash-grouper-real.cfg 0x3a40000\n\nimport re, sys, struct, zlib, uuid, pathlib\n\nSEC          = 512\nBOOT_SECTORS = 8192          # boot0 + boot1, 2 MiB each -&gt; 4 MiB\nENTRIES, ENTRY_SIZE = 128, 128\nTYPE_GUID = uuid.UUID(\"EBD0A0A2-B9E5-4433-87C0-68B6B72699C7\").bytes_le\n\n# Not real Linux partitions: BCT and PT live in the eMMC boot area, and GPT\n# is the container we are about to fill.\nSKIP = {\"BCT\", \"PT\", \"GPT\"}\n\ncfg  = pathlib.Path(sys.argv[1] if len(sys.argv) &gt; 1 else \"flash-grouper-real.cfg\")\nemmc = int(sys.argv[2], 0) if len(sys.argv) &gt; 2 else 0x3a40000\n\n# --- parse flash.cfg, rebuilding the sequential layout nvflash produced ----\nparts, offset = [], 0\nfor block in cfg.read_text().split(\"[partition]\")[1:]:\n    name = re.search(r\"^name=(\\S+)\",  block, re.M).group(1)\n    size = int(re.search(r\"^size=(\\S+)\", block, re.M).group(1), 0)\n    parts.append((name, offset, size))\n    offset += size\n\ntotal = offset\nprint(f\"eMMC user area : {emmc*SEC:,} bytes ({emmc:,} sectors of {SEC})\")\nprint(f\"flash.cfg total: {total:,} bytes\")\nif total &gt; emmc * SEC:\n    sys.exit(f\"ERROR: layout overshoots the device by {total - emmc*SEC:,} bytes\")\n\n# --- convert to kernel space ------------------------------------------------\nBOOT_BYTES = BOOT_SECTORS * SEC\nkern = []\nprint(f\"\\n{'part':5} {'nvflash byte':&gt;15} {'kernel LBA':&gt;11} {'..':^4} {'end':&gt;10}\")\nprint(\"-\" * 52)\nfor name, nv_off, size in parts:\n    if name in SKIP:\n        continue\n    k = nv_off - BOOT_BYTES\n    if k &lt; 0:\n        print(f\"{name:5} {nv_off:&gt;15,}   inside eMMC boot area, skipped\")\n        continue\n    s, e = k // SEC, (k + size) // SEC - 1\n    kern.append((name, s, e))\n    print(f\"{name:5} {nv_off:&gt;15,} {s:&gt;11,} {'..':^4} {e:&gt;10,}\")\n\ngpt_k = emmc - BOOT_SECTORS - 1        # gpt_sector, KERNEL LBA\nent_k = gpt_k - 32\nfor name, s, e in kern:\n    if e &gt; gpt_k:\n        sys.exit(f\"ERROR: {name} ends at {e}, past gpt_sector {gpt_k}\")\n\n# --- build the structure ----------------------------------------------------\nent = bytearray(ENTRIES * ENTRY_SIZE)\nfor i, (name, s, e) in enumerate(kern):\n    b  = TYPE_GUID + uuid.uuid5(uuid.NAMESPACE_DNS, f\"grouper-{name}\").bytes_le\n    b += struct.pack(\" $cur\"; return 0; }\n        fi\n        sleep 0.5\n    done\n    note \"no re-enumeration after exploit\"; return 1\n}\n\nattempt() {\n    TRY=$((TRY+1))\n    note \"----- attempt #$TRY : $(lsusb | grep -i 0955:) -----\"\n    local before; before=$(devnum)\n\n    python3 \"$D/fusee-launcher/fusee-launcher.py\" \\\n        \"$D/payload/uart_payload_n7.bin\" -P 7330 2&gt;&amp;1 \\\n        | tee -a \"$L\" | grep -q \"Launch complete\" \\\n        || { note \"exploit refused (RCM session already used)\"; return 2; }\n    note \"payload injected\"\n    wait_reenum \"$before\" || return 2\n\n    # No --go: keeps the nv3pserver alive so we can write the GPT next.\n    \"$N\" --bct \"$B/bct-blank.bin\" --setbct \\\n         --configfile \"$B/flash-grouper-real.cfg\" --create \\\n         --bl \"$D/bootloader/bootloader-grouper-4.23.img\" 2&gt;&amp;1 \\\n        | grep -vE \"[0-9]+/[0-9]+ bytes\" | tee -a \"$L\"\n    [ \"${PIPESTATUS[0]}\" -eq 0 ] || { note \"--create failed\"; return 1; }\n\n    note \"writing GPT at 4096-sector $GPT_SECTOR_4K\"\n    \"$N\" --resume --rawdevicewrite \"$GPT_SECTOR_4K\" 5 \"$B/gpt-kernel.bin\" 2&gt;&amp;1 \\\n        | grep -vE \"[0-9]+/[0-9]+ bytes\" | tee -a \"$L\"\n    \"$N\" --resume --rawdeviceread  \"$GPT_SECTOR_4K\" 5 \"$B/gpt-readback.bin\" \\\n        &gt;/dev/null 2&gt;&amp;1\n    cmp -s \"$B/gpt-kernel.bin\" \"$B/gpt-readback.bin\" \\\n        &amp;&amp; note \"GPT verified byte-identical\" \\\n        || { note \"GPT READ-BACK MISMATCH\"; return 1; }\n\n    note \"booting\"\n    \"$N\" --resume --go &gt;/dev/null 2&gt;&amp;1\n    return 0\n}\n\nwhile left; do\n    if apx; then\n        attempt; rc=$?\n        [ \"$rc\" -eq 0 ] &amp;&amp; { note \"=== DONE. Expect the boot animation in ~1 min,\"\n                             note \"    then 7-10 min of dex2oat. ===\"; exit 0; }\n        note \"Power-cycle: unplug USB, hold Power 15 s, hold Volume Up, replug.\"\n        while apx  &amp;&amp; left; do sleep 1; done\n    fi\n    while ! apx &amp;&amp; left; do sleep 1; done\n    sleep 3\ndone\nnote \"=== timed out after $TRY attempt(s) ===\"\nexit 1\n\n\n================================================================================\n12. DISPROVEN HYPOTHESES \u2014 DO NOT WASTE TIME ON THESE\n================================================================================\n\n\"No blob.bin / no SBK backup = unrecoverable.\"\n    False. Fus\u00e9e Gel\u00e9e plus the patched miniloader handles it. The SBK is never\n    needed: the hardware AES engine uses the fused key without revealing it.\n\n\"You need a second Nexus 7 2012.\"\n    False for this method.\n\n\"Google's grouper bootloader image is broken\" (postmarketOS wiki).\n    Not supported by evidence. It is bit-identical to the one tegra30_debrick\n    ships and uses to un-brick devices.\n\n\"Low battery / NvError 0x0.\"\n    Real failure mode, but not mine. Battery read 100 % / 4.164 V throughout.\n\n\"Worn-out eMMC\" (the classic grouper death).\n    Ruled out here: no partition-table errors, every write verified.\n\n\"The display is dead.\"\n    No. The bootloader paints the panel. Nothing repaints it because Android\n    never gets past init. Also: in APX the display is never touched, so the\n    panel keeps a stale frame \u2014 APX and a hung boot look IDENTICAL on screen.\n\n\"MTP enumeration (18d1:4e41) proves Android booted.\"\n    False, and it fooled me for an hour. init applies\n    persist.sys.usb.config=mtp long before the framework starts. Only\n    18d1:4e42 plus a working `adb shell` proves userspace is alive.\n\n\"AMD USB controllers cannot run Fus\u00e9e Gel\u00e9e.\"\n    Overstated. With old_scheme_first=Y it worked every single time on a\n    Ryzen 7 7840HS.\n\n\"ueventd is not creating /dev/block nodes.\"\n    My own wrong turn. /dev exists; SELinux merely blocks adbd from reading it,\n    and the sync protocol reports that as ENOENT. Verified via /sys/dev/block.\n\n\"The sector size is 4096 so my LBAs are wrong.\"\n    No. logical_block_size is 512. The 4096 figure is nvflash's own sector\n    granularity for --rawdevice*, and BytesPerSector in --getpartitiontable.\n\n\"nvflash --create writes the GPT.\"\n    It does not. It allocates the region and leaves it full of zeros. Verified\n    by reading the partition back: 524,288 bytes, zero non-zero bytes.\n\n\n================================================================================\n13. FULL CHRONOLOGY OF THE SESSION\n================================================================================\n\nDay 1\n  Start: tablet enumerates as 0955:7330 APX on plain USB connect. No fastboot,\n  no adb, black screen for months after a failed flash-all over postmarketOS.\n\n  - USB would not enumerate at all (-110/-71/-62). Fixed with\n    old_scheme_first + initial_descriptor_timeout.\n  - Followed the repo README: exploit OK, --setbct OK, --download EBT reported\n    \"sent successfully\". Tablet chain-booted an old Android from RAM. Looked\n    like victory. Cold boot -&gt; back to APX. The BCT had never been written.\n  - Tried --sync as the README suggests. No effect; verified by reading the BCT\n    partition back.\n  - Discovered the BCT ships with BootLoadersUsed=1 -&gt; \"BCT is full\". Blanked\n    the slot. --create then succeeded and THE TABLET COLD-BOOTED to the\n    bootloader splash. Brick solved.\n  - Immediately destroyed it again by opening a plain --setbct session to write\n    the system image. Learned rule (2).\n  - Stuck at the Google logo. Interpreted MTP enumeration as \"Android booted\".\n    Wrong.\n\nDay 2\n  - Patched the ramdisk for unauthenticated adb. Got a diagnostic channel.\n  - `exec '/system/bin/sh' failed` -&gt; /system not mounted.\n  - /proc/partitions: no partitions at all. /proc/last_kmsg gave the kernel\n    command line and \"mmcblk0: unknown partition table\".\n  - Read the GPT partition back after --create: 524,288 bytes of zeros.\n    nvflash never writes it.\n  - Wrote a hand-built GPT at gpt_sector. Rejected. Wrote it at last_lba too.\n    Rejected. Validated the structure with parted on the host: perfect.\n  - Wrote a classic MBR at sector 0 plus a rewritten fstab. Rejected.\n  - Ruled out ueventd, sector size, and missing parsers (kallsyms shows\n    efi_partition and msdos_partition both present).\n  - Searched upstream: found Dmitry Osipenko's Tegra Partition Table series and\n    the sentence about boot0-&gt;boot1-&gt;main linearisation. Recomputed everything\n    in kernel space, rewrote the GPT, verified the read-back.\n  - Boot: nine partitions appeared, /system /cache /data mounted, four spinning\n    dots, dex2oat over every APK, sys.boot_completed=1 at 416 s.\n  - Setup wizard on screen. Done.\n\nElapsed: about two days, most of it on the 8192-sector offset.\n\n\n================================================================================\n14. REMAINING WORK / NOTES\n================================================================================\n\n- The boot.img currently flashed on my device is the PATCHED one from section 8\n  (ro.secure=0, ro.adb.secure=0, busybox in the ramdisk). That is an\n  unauthenticated root adb channel and should be replaced by the stock image\n  once debugging is over:\n        adb reboot bootloader\n        fastboot flash boot boot.img\n        fastboot reboot\n\n- userdata was created empty and formatted by nvflash as ext3; Android mounted\n  it as ext4 and populated it without complaint. 26.9 GiB free.\n\n- The GPT written here omits EBT because it straddles the boot area. If you\n  need a by-name/EBT link for some tool, you cannot have one \u2014 flash the\n  bootloader through fastboot or nvflash instead.\n\n- Custom ROMs: with a correct GPT and a working bootloader the device is a\n  normal unlocked Nexus 7 again. LineageOS 14.1 (7.1.2) exists for grouper.\n  Any custom recovery/ROM will expect the by-name layout, which now works.\n  Note that a ROM's own repartitioning tools may not understand the boot\n  offset \u2014 be careful with anything that rewrites the partition table.\n\n- If you re-run --create for any reason, YOU MUST REWRITE THE GPT AFTERWARDS.\n  --create wipes it.\n\n\n================================================================================\n15. SOURCES\n================================================================================\n\nRecovery tool\n  https://github.com/tofurky/tegra30_debrick\n\nThe sentence that solved it (eMMC boot0-&gt;boot1-&gt;main linearisation)\n  https://lkml.rescloud.iu.edu/hypermail/linux/kernel/2005.2/00406.html\n  [PATCH v6 3/7] partitions: Introduce NVIDIA Tegra Partition Table\n  by Dmitry Osipenko\n\ngpt_sector semantics and the eMMC-sectors-minus-boot-sectors formula\n  https://patchwork.ozlabs.org/project/linux-tegra/patch/20200219162339.16192-1-digetx@gmail.com/\n  https://lkml.iu.edu/hypermail/linux/kernel/2005.2/00407.html\n  https://lwn.net/Articles/820643/\n\nDownstream grouper kernel source (efi.c with force_gpt_sector)\n  https://android.googlesource.com/kernel/tegra.git/+/android-tegra3-grouper-3.1-jb-fr1/fs/partitions/efi.c\n\nFactory images\n  https://developers.google.com/android/images#nakasi\n\nOther references\n  https://docs.u-boot.org/en/latest/board/asus/grouper.html\n  https://wiki.postmarketos.org/wiki/Google_Nexus_7_2012_(asus-grouper)\n  https://www.ifixit.com/Guide/Nexus+7+Battery+Replacement/9895\n\n================================================================================\n If this helped you, consider replying wherever you found it \u2014 this device is\n old enough that the surviving documentation is thin, and the boot-offset trap\n appears to be written down nowhere else.\n================================================================================\n", "creation_timestamp": "2026-08-22T18:16:45.132799Z"}