Unbricking SHIELD TV (2015) with a Bootrom Exploit

Last year, a friend gave me his SHIELD TV when he moved. He worked at NVIDIA and got it for free and had used it only a handful of times before it traveled from his closet to my own. I had forgotten about it until I had a need for a Raspberry Pi and discovered that they were all still sold out. Wouldn’t the SHIELD TV make a great RPI replacement? It has been out for almost a decade now and surely people have gotten Linux working on it. After following a guide from 2015, I quickly bricked the device trying to flash an outdated DTB file. It turns out that a bad DTB brick was quite common in the community and unfortunately the only proposed solution of “return it to Best Buy” was not an option for me. Even though I have never cared about this device, I was still ashamed at my negligence and felt guilty about creating more e-waste. Thus began my journey to recover the device.

Serial port

I needed to figure out why the device was not booting and a UART console usually provided diagnostics information. Some devices (such as the Kindle) even have recovery options available over the serial port. A quick probing of suspicious looking pads on the logic board yielded no results but luckily, people in the Linux thread have already found the pins. I was able to solder some wires to the pins and get a serial console.

Pins soldered

Pins soldered (zoomed out)

Serial connected

Unfortunately, there was no recovery option available in the console but from the logs I was able to understand the reason why the SHIELD TV no longer booted. I flashed a DTB (device tree blob) designed for an older version of cboot. Therefore, when cboot tries to setup some device with the wrong configuration, it ends up in a dead-loop.

APX Mode

On Tegra devices, there is an emergency recovery option called APX mode. This is implemented in the boot ROM (so it can never be corrupted) and uses NVIDIA’s RCM protocol to communicate. According to some reverse engineering done by @ktemkin, there are three ways of entering APX mode:

  1. The ROM fails to find a valid boot-loader.
  2. Some GPIO is pulled to a particular value (e.g. holding some button combination while booting).
  3. A scratch register is written to by software before rebooting.

Of these options, 1 is unavailable because the boot-loader is indeed valid (and signed) and the processor dies while already running that boot-loader. I spent some time with 2 by trying to short a random sampling of pads on the logic board while attempting to boot (with no luck). 3 is not an option because we need to run software. After chatting with famous Switch hacker @plutoo, he suggested I short out the eMMC while booting to force condition 1. This seemed like a promising route because I remember seeing some unfilled pads near the eMMC. I used a pin to short one of them to the shielding near it and saw the APX device show up on my computer. To make things less painful, I soldered a piece of wire to one of the pads and attached it to a pin I can easily ground.

eMMC soldered

At this point, you may wonder why I didn’t just re-flash the eMMC and the main reason is that I didn’t have the patience to find the correct signals (CMD/CLK/DAT0) and solder to those tiny pads all clumped up together (I also don’t own any micro/nano soldering equipment so I have to do everything with a standard tip iron). The second reason is that it would have made for a boring blog post 😅.


Getting into APX mode was the first step. NVIDIA designed the USB RCM protocol to only accept signed messages on production fused devices. The key is held by the device manufacturer, which means that only NVIDIA factories are allowed to unbrick the SHIELD TV. However, a few years ago, some researchers discovered a vulnerability in the boot ROM USB stack in the same Tegra X1 chip used in the SHIELD TV and used it to hack the Nintendo Switch. This vulnerability can be exploited to run arbitrary code in the boot processor. The Reswitched team has released a tool to exploit this vulnerability and some other people have since modified the tool to debrick older Tegra devices.

Putting it all together…

The plan is as follows:

  1. Enter APX mode by grounding a random eMMC pin while powering on in order to force the boot-loader read to fail.
  2. Use “Fusée Launcher” to launch a payload which patches out the RCM signature checks.
  3. Use NVIDIA’s Linux4Tegra tools to flash the correct DTB over USB RCM.

Patching RCM security checks

The original “Fusée Launcher” did not work on my SHIELD TV likely due to some hard coded offset differences with the Nintendo Switch. Instead, I used the fork from @jevinskie for older Tegra devices which added an offset finder (leaked from dumping a stack frame). I patched in the original offsets for the X1:

diff --git a/fusee-launcher.py b/fusee-launcher.py
index 592bf4e..afbfa61 100755
--- a/fusee-launcher.py
+++ b/fusee-launcher.py
@@ -46,15 +46,15 @@ RCM_V4P_HEADER_SIZE = 680
 # The address where the RCM payload is placed.
 # This is fixed for most device.
-RCM_PAYLOAD_ADDR    = 0x4000A000
+RCM_PAYLOAD_ADDR    = 0x40010000
 # The address where the user payload is expected to begin.
 # Specify the range of addresses where we should inject our
 # payload address.
-STACK_SPRAY_END     = 0x40011000
+STACK_SPRAY_START   = 0x40014E40
+STACK_SPRAY_END     = 0x40017000
 class RCMError(Exception):
     def __init__(self, rcm_error_code):
@@ -467,8 +467,8 @@ class RCMHax:
     DEFAULT_PID = 0x7330
     # Exploit specifics
-    COPY_BUFFER_ADDRESSES   = [0x40003000, 0x40005000]   # The addresses of the DMA buffers we can trigger a copy _from_.
-    STACK_END               = 0x4000A000                 # The address just after the end of the device's stack.
+    COPY_BUFFER_ADDRESSES   = [0x40005000, 0x40009000]   # The addresses of the DMA buffers we can trigger a copy _from_.
+    STACK_END               = 0x40010000                 # The address just after the end of the device's stack.
     def __init__(self, wait_for_device=False, os_override=None, vid=None, pid=None, override_checks=False):
         """ Set up our RCM hack connection."""
@@ -725,7 +725,7 @@ with open("intermezzo_patched.bin", "wb") as f:
 # Prefix the image with an RCM command, so it winds up loaded into memory
 # at the right location (0x40010000).
 # Use the maximum length accepted by RCM, so we can transmit as much payload as
 # we want; we'll take over before we get to the end.

Next, I downloaded @ktemkin’s non-secure RCM enable payload and put it in the same directory. I also made the following changes (the original patch did not cover some other code paths that checks the security fuse bit):

diff --git a/ipatch_rcm_sample.c b/ipatch_rcm_sample.c
index 491cb60..b797c2f 100644
--- a/ipatch_rcm_sample.c
+++ b/ipatch_rcm_sample.c
@@ -7,9 +7,13 @@
 #include <stdint.h>
-#include "registers.h"
 #include "t210.h"
+#define _REG(base, off) *(volatile unsigned int *)((base) + (off))
+#define reg_write(base, off, value) _REG(base, off) = value
+#define reg_clear(base, off, value) _REG(base, off) &= ~value
+#define reg_set(base, off, value) _REG(base, off) |= value
+#define reg_read(base, off) _REG(base, off)
 #define HEX_CHAR(x) ((((x) + '0') > '9') ? ((x) + '7') : ((x) + '0'))
@@ -262,7 +266,7 @@ void unipatch_word(uint8_t slot)
  * Example exploit payload; printks the SBK and enables unsigned RCM.
-void main()
+void __attribute__((section(".entry"))) main()
   entry_point start;
@@ -271,8 +275,8 @@ void main()
   puts("Hello from the early X1 bootROM!\n");
   puts("Attempting to patch over the IROM itself.\n");
-  // Patch the getSecurityMode function to always return 3 (production non-secure).
-  ipatch_word(10, 0x102050, 0x2000 | DESIRED_SECURITY_MODE);
+  // Patch the get_fusebit_aec_2 function to always return 1
+  ipatch_word(10, 0x100286, 0xE003); // B 0xA
   // Jump into the recovery mode routine.
   puts("I'm going to go ahead and run RCM for you with security off. Have fun. :)\n");

Finally, I modified the Makefile in fusee-launcher to build the new payload:

diff --git a/Makefile b/Makefile
index 4f9a7df..ef34489 100644
--- a/Makefile
+++ b/Makefile
@@ -23,9 +23,9 @@ CFLAGS = \
-all: intermezzo.bin dump-sbk-via-usb.bin
+all: intermezzo.bin dump-sbk-via-usb.bin ipatch_rcm_sample.bin
+ENTRY_POINT_ADDRESS := 0x40010000
 # Provide the definitions used in the intermezzo stub.
@@ -43,6 +43,12 @@ dump-sbk-via-usb.elf: dump-sbk-via-usb.o
 dump-sbk-via-usb.o: dump-sbk-via-usb.S
    $(CC) $(CFLAGS) $(DEFINES) $< -c -o $@
+ipatch_rcm_sample.elf: ipatch_rcm_sample.o
+   $(LD) -T ipatch_rcm_sample.lds --defsym LOAD_ADDR=$(ENTRY_POINT_ADDRESS) $(LDFLAGS) $^ -o $@
+ipatch_rcm_sample.o: ipatch_rcm_sample.c
+   $(CC) $(CFLAGS32) $(DEFINES) $< -c -o $@
 %.bin: %.elf
    $(OBJCOPY) -v -O binary $< $@
diff --git a/ipatch_rcm_sample.lds b/ipatch_rcm_sample.lds
new file mode 100644
index 0000000..2ac20f8
--- /dev/null
+++ b/ipatch_rcm_sample.lds
@@ -0,0 +1,22 @@
+   . = LOAD_ADDR;
+   .text : {
+       *(.entry)
+       *(.text)
+   }
+   /* always end on a word boundary for our copy */
+   . = ALIGN(4);
+   /DISCARD/ : { *(.dynstr*) }
+   /DISCARD/ : { *(.dynamic*) }
+   /DISCARD/ : { *(.plt*) }
+   /DISCARD/ : { *(.interp*) }
+   /DISCARD/ : { *(.gnu*) }
+   /DISCARD/ : { *(.data*) }

Then make clean; make will build the payload and ./fusee-launcher.py -w -V 0x0955 -P 0x7721 ipatch_rcm_sample.bin will run it (after waiting for the APX device to show up). The serial port should print out:

Hello from the early X1 bootROM!                                                
Attempting to patch over the IROM itself.                                       
I'm going to go ahead and run RCM for you with security off. Have fun. :)       
For reference, your SBK+DK are: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Extracting the firmware

Next, I needed the stock firmware to recover to. Luckily, NVIDIA provides stock recovery images for the SHIELD TV. I downloaded the 9.0.0 recovery image for my SHIELD TV (2015) and extracted the .zip file. Next, I had to extract two files from blob which contains the boot-loaders and other data. Opening the file in a hex editor, we see the name of the partitions in ASCII listed in order. By guessing and checking I discovered the structure for each entry is something like:

blob in hex editor

struct blob_entry {
    char name[4];
    char unknown[36];
    uint32_t offset;
    uint32_t length;
    uint32_t unknown2;

I only care about two entries: EBT (the cboot boot-loader used by tegrarcm) and DTB (the partition I corrupted) so there was no need to write a script. I hand-extracted EBT as shield-9.0.0-cboot.bin and both DTB as shield-9.0.0-dtb1.bin and shield-9.0.0-dtb2.bin.

Using tegraflash.py

Next, I downloaded Linux for Tegra R24.2.3 driver package which contains the tegraflash.py tool. I placed the files I extracted from the firmware in /bootloader.

I first dumped the corrupted DTB:

$ ./tegraflash.py --bl shield-9.0.0-cboot.bin --applet nvtboot_recovery.bin --chip 0x21 --cmd "read DTB dtb-extracted.bin"
Welcome to Tegra Flash
version 1.0.0
Type ? or help for help and q or quit to exit
Use ! to execute system commands
[   0.0013 ] Generating RCM messages
[   0.0026 ] tegrarcm --listrcm rcm_list.xml --chip 0x21 --download rcm nvtboot_recovery.bin 0 0
[   0.0038 ] RCM 0 is saved as rcm_0.rcm
[   0.0049 ] RCM 1 is saved as rcm_1.rcm
[   0.0050 ] List of rcm files are saved in rcm_list.xml
[   0.0050 ] 
[   0.0050 ] Signing RCM messages
[   0.0060 ] tegrasign --key None --list rcm_list.xml --pubkeyhash pub_key.key
[   0.0070 ] Assuming zero filled SBK key
[   0.0111 ] 
[   0.0111 ] Copying signature to RCM mesages
[   0.0123 ] tegrarcm --chip 0x21 --updatesig rcm_list_signed.xml
[   0.0139 ] 
[   0.0140 ] Boot Rom communication
[   0.0149 ] tegrarcm --chip 0x21 --rcm rcm_list_signed.xml
[   0.0158 ] BR_CID: 0x32101001640ca58800000000030184c0
[   0.0167 ] RCM version 0X210001
[   0.0174 ] Boot Rom communication completed
[   1.0277 ] 
[   1.0278 ] Retrieving storage infomation
[   1.0287 ] tegrarcm --oem platformdetails storage storage_info.bin
[   1.0296 ] Applet version 00.01.0000
[   1.0363 ] Saved platform info in storage_info.bin
[   1.0866 ] 
[   1.0866 ] Reading BCT from device for further operations
[   1.0866 ] Sending bootloader and pre-requisite binaries
[   1.0880 ] tegrarcm --download ebt shield-9.0.0-cboot.bin 0 0
[   1.0893 ] Applet version 00.01.0000
[   1.0974 ] Sending ebt
[   1.0994 ] [................................................] 100%
[   1.4005 ] 
[   1.4018 ] tegrarcm --boot recovery
[   1.4028 ] Applet version 00.01.0000
[   1.4119 ] 
[   1.4120 ] Reading partition
[   1.4131 ] tegradevflash --read DTB /home/parallels/Downloads/Linux_for_Tegra_R24.2.3/bootloader/dtb-extracted.bin
[   1.4145 ] Cboot version 00.01.0000
[   2.0899 ] Reading partition DTB in file /home/parallels/Downloads/Linux_for_Tegra_R24.2.3/bootloader/dtb-extracted.bin
[   2.0907 ] [................................................] 100%
[   3.2296 ] 

Because the broken DTB I flashed was smaller than the working one, I was able to find the remaining bytes of the original DTB in the dump. I used this to match the bytes up to shield-9.0.0-dtb2.bin which I then flashed:

$ ./tegraflash.py --bl shield-9.0.0-cboot.bin --applet nvtboot_recovery.bin --chip 0x21 --cmd "write DTB shield-9.0.0-dtb2.bin"
Welcome to Tegra Flash
version 1.0.0
Type ? or help for help and q or quit to exit
Use ! to execute system commands
[   0.0016 ] Generating RCM messages
[   0.0030 ] tegrarcm --listrcm rcm_list.xml --chip 0x21 --download rcm nvtboot_recovery.bin 0 0
[   0.0043 ] RCM 0 is saved as rcm_0.rcm
[   0.0052 ] RCM 1 is saved as rcm_1.rcm
[   0.0052 ] List of rcm files are saved in rcm_list.xml
[   0.0052 ] 
[   0.0052 ] Signing RCM messages
[   0.0063 ] tegrasign --key None --list rcm_list.xml --pubkeyhash pub_key.key
[   0.0071 ] Assuming zero filled SBK key
[   0.0117 ] 
[   0.0117 ] Copying signature to RCM mesages
[   0.0131 ] tegrarcm --chip 0x21 --updatesig rcm_list_signed.xml
[   0.0147 ] 
[   0.0140 ] Boot Rom communication
[   0.0149 ] tegrarcm --chip 0x21 --rcm rcm_list_signed.xml
[   0.0158 ] BR_CID: 0x32101001640ca58800000000030184c0
[   0.0167 ] RCM version 0X210001
[   0.0174 ] Boot Rom communication completed
[   0.0613 ] 
[   0.0614 ] Sending bootloader and pre-requisite binaries
[   0.0626 ] tegrarcm --download ebt shield-9.0.0-cboot.bin 0 0
[   0.0635 ] Applet version 00.01.0000
[   0.0690 ] Sending ebt
[   0.0692 ] [................................................] 100%
[   0.4042 ] 
[   0.4054 ] tegrarcm --boot recovery
[   0.4064 ] Applet version 00.01.0000
[   0.4142 ] 
[   0.4143 ] Writing partition
[   0.4154 ] tegradevflash --write DTB /home/parallels/Downloads/Linux_for_Tegra_R24.2.3/bootloader/shield-9.0.0-dtb2.bin
[   0.4163 ] Cboot version 00.01.0000
[   1.0926 ] Writing partition DTB with /home/parallels/Downloads/Linux_for_Tegra_R24.2.3/bootloader/shield-9.0.0-dtb2.bin
[   1.0933 ] [................................................] 100%
[   1.1776 ] 

Here is the console output when flashing the DTB:

[0171.228] Enabled early print                                                  
[0171.231] [TegraBoot] (version 24.00.2015.42-mobile-5e019235)                  
[0171.237] Processing in recovery mode                                          
[0171.240] A02 Bootrom Patch rev = 31                                           
[0171.243] Power-up reason: pmc por                                             
[0171.247] Established communication link with host                             
[0174.689] Odmdata from BCT: 0x00294000                                         
[0174.693] DebugPort= 0x3                                                       
[0174.695] BoardId read from EEPROM/NCT: 2530                                   
[0174.723] max77620 set reest time to 0x00000078                                
[0174.728] Turned on fan for loki board                                         
[0174.731] Turning on Foster LED                                                
[0174.734] Checking BUTTON_VOL_UP Pin                                           
[0174.738] No Battery Present                                                   
[0174.740] RamCode = 2                                                          
[0174.743] Platform has Ddr4 type ram                                           
[0174.746] max77620 disabling SD1 Remote Sense                                  
[0174.750] Setting Ddr voltage to 1125mv                                        
[0174.754] Serial Number of Pmic Max77663: 0x2301a9                             
[0174.762] Entering ramdump check                                               
[0174.765] Get RamDumpCarveOut = 0xff280000                                     
[0174.769] RamDumpCarveOut=0xff280000,  RamDumperFlag=0x0                       
[0174.774] Last reboot was clean, booting normally!                             
[0174.779] Sdram initialization is successful                                   
[0174.783] SecureOs Carveout Base=0xff800000 Size=0x00800000                    
[0174.789] GSC1 Carveout Base=0xff700000 Size=0x00100000                        
[0174.794] GSC2 Carveout Base=0xff600000 Size=0x00100000                        
[0174.799] GSC3 Carveout Base=0xff500000 Size=0x00100000                        
[0174.804] GSC4 Carveout Base=0xff400000 Size=0x00100000                        
[0174.809] GSC5 Carveout Base=0xff300000 Size=0x00100000                        
[0174.814] BpmpFw Carveout Base=0xff2c0000 Size=0x00040000                      
[0174.820] Lp0 Carveout Base=0xff2bf000 Size=0x00001000                         
[0174.835] RamDump Carveout Base=0xff23f000 Size=0x00080000                     
[0174.841] Platform-DebugCarveout: 0                                            
[0174.963] Downloaded bootloader successfully at 0x92c00000                     
[0174.988] Using GPT Primary to query partitions                                
[0175.005] Bootloader DTB Load Address: 0xedfe0dd0                              
[0175.010] MAX77620_GPIO1 Configured.                                           
[0175.013] MAX77620_GPIO5 Configured.                                           
[0175.017] CPU power rail is up                                                 
[0175.020] CPU clock enabled                                                    
[0175.023] Performing RAM repair                                                
[0175.026] Updating A64 Warmreset Address to 0x92c002e9                         
[0175.033] Enable APE clock/reset                                               
[0175.036] Error in NvTbootGetTOSBinaryLength: 0x11 !                           
[0175.040] Loading Secure OS image failed.                                      
[0175.044] Set NvDecSticky Bits                                                 
[0175.048] GSC1 address : ff700000                                              
[0175.051] GSC2 address ff63fffc value c0edbbcc                                 
[0175.056] GSC2 address : ff600000                                              
[0175.060] GSC3 address : ff500000                                              
[0175.063] GSC4 address : ff400000                                              
[0175.067] GSC5 address : ff300000                                              
[0175.070] GSC MC Settings done                                                 
[0175.073] NvTbootPackSdramParams: start.                                       
[0175.078] NvTbootPackSdramParams: done.                                        
[0175.082] Next binary entry address: 0x92c00258                                
[0175.087] BoardId: 2530                                                        
[0175.111] NvTbootI2cProbe(): error code 0x00045100 Error while read            
[0175.117] Display board id is not available                                    
[0175.121] dram memory type is 3                                                
[0175.124] Tegraboot started after 175338183 us                                 
[0175.129] Basic modules init took 3854605 us                                   
[0175.133] USB data transfer took -179192788 us                                 
[0175.137] Validation of images took 179291610 us                               
[0175.142] Carveout took 1 us                                                   
[0175.144] CPU initialization took 36048 us                                     
[0175.148] Total time taken by TegraBoot 3989476 us                             
[0175.153] Starting CPU & Halting co-processor                                  
[0179.367] Debug Init done                                                      
[0179.370] Marked DTB cacheable                                                 
[0179.373] Bootloader DTB loaded at 0x83000000                                  
[0179.377] DeviceTree Init done                                                 
[0179.390] Pinmux applied successfully                                          
[0179.395] gicd_base: 0x50041000                                                
[0179.399] gicc_base: 0x50042000                                                
[0179.402] Interrupts Init done                                                 
[0179.407] Using base:0x60005008 & irq:33 for tick-timer                        
[0179.412] Using base:0x60005000 for delay-timer                                
[0179.416] platform_init_timer: DONE                                            
[0179.420] Timer(tick) Init done                                                
[0179.424] osc freq = 38400 khz                                                 
[0179.430] welcome to cboot                                                     
[0179.433] Cboot Version: 24.00.2015.42-t210-236e8a1e                           
[0179.438] calling constructors                                                 
[0179.441] initializing heap                                                    
[0179.444] initializing threads                                                 
[0179.447] initializing timers                                                  
[0179.450] creating bootstrap completion thread                                 
[0179.454] top of bootstrap2()                                                  
[0179.457] CPU: ARM Cortex A57                                                  
[0179.460] CPU: MIDR: 0x411FD071, MPIDR: 0x80000000                             
[0179.465] initializing platform                                                
[0179.510] config for ddr50 mode completed                                      
[0179.514] sdmmc bdev is already initialized                                    
[0179.518] of_register: registering tegra_udc to of_hal                         
[0179.523] of_register: registering inv20628-driver to of_hal                   
[0179.529] of_register: registering ads1015-driver to of_hal                    
[0179.534] of_register: registering lp8557-bl-driver to of_hal                  
[0179.540] of_register: registering bq2419x_charger to of_hal                   
[0179.546] of_register: registering cpc to of_hal                               
[0179.550] of_register: registering bq27441_fuel_gauge to of_hal                
[0179.565] gpio framework initialized                                           
[0179.569] of_register: registering tca9539_gpio to of_hal                      
[0179.574] of_register: registering tca9539_gpio to of_hal                      
[0179.580] of_register: registering i2c_bus_driver to of_hal                    
[0179.585] of_register: registering i2c_bus_driver to of_hal                    
[0179.591] of_register: registering i2c_bus_driver to of_hal                    
[0179.596] pmic framework initialized                                           
[0179.600] of_register: registering max77620_pmic to of_hal                     
[0179.605] regulator framework initialized                                      
[0179.609] of_register: registering tps65132_bl_driver to of_hal                
[0179.615] of_register: registering tegra_xhci to of_hal                        
[0179.620] initializing target                                                  
[0179.627] gpio_driver_register: register 'tegra_gpio_driver' driver            
[0179.637] fixed regulator driver initialized                                   
[0179.678] initializing OF layer                                                
[0179.682] of_children_init: Ops found for compatible string nvidia,tegra210-xhi
[0179.690] of_children_init: Ops found for compatible string nvidia,tegra210-i2c
[0179.712] I2C Bus Init done                                                    
[0179.714] of_children_init: Ops found for compatible string nvidia,tegra210-i2c
[0179.727] I2C Bus Init done                                                    
[0179.730] of_children_init: Ops found for compatible string nvidia,tegra210-i2c
[0179.742] I2C Bus Init done                                                    
[0179.745] of_children_init: Ops found for compatible string nvidia,tegra210-i2c
[0179.758] I2C Bus Init done                                                    
[0179.761] of_children_init: Ops found for compatible string nvidia,tegra210-i2c
[0179.773] I2C Bus Init done                                                    
[0179.776] of_children_init: Ops found for compatible string maxim,max77620     
[0179.789] max77620_init using irq 118                                          
[0179.794] register 'maxim,max77620' pmic                                       
[0179.798] gpio_driver_register: register 'max77620-gpio' driver                
[0179.804] of_children_init: Ops found for compatible string nvidia,tegra210-i2c
[0179.817] I2C Bus Init done                                                    
[0179.821] calling apps_init()                                                  
[0179.834] Found 29 GPT partitions in "sdmmc3_user"                             
[0179.839] Proceeding to flashing Server                                        
[0179.843] usbdcd_reinit Initialize driver to use already enumerated device     
[0179.850] nv3p_priv_usbf_open USB configuration success                        
[0179.859] Writing DTB partition                                                
[0179.862] bct_init bctinit                                                     
[0179.874] bct_init bctinit                                                     
[0179.877] bct_init bctinit                                                     
[0179.938] partition DTB write successful.

With that, I was able to recover successfully and boot back into fastboot and Android TV. (The first time in my life I was happy to see the Android logo.)

Some other notes for anyone who wishes to attempt this:

  • With the eMMC pin I randomly picked to short, it takes about 100 seconds to get into APX mode (likely due to some retry timeout or watchdog routine). A different pin made the transition instant but was harder to solder.
  • When running tegrarcm --chip 0x21 --rcm rcm_list_signed.xml, if you get RCM version 0X210001 that means the ipatch was successful. If you get RCM version 0X13, that’s actually an error code for invalid public key (the ipatch failed or you didn’t run it). If you get no RCM version and Boot Rom communication failed, you need to retry. I’m not exactly sure why sometime it ends up in Boot Rom communication failed but the patch isn’t perfect.
  • You may have to restart after executing any command.
  • Trying to use the cboot.bin included in the L4T drivers package fails to boot.
  • Thanks to @adeljck for the Tegra X1 bootrom dump and IDC file which means I didn’t have to RE any code myself.


  1. Yifan Lu

    That’s a good point. It’s probably like char name[40] but since I only care about the offset and length, it doesn’t matter what the other fields are.

Leave a Comment

Your email address will not be published. Required fields are marked *