@@ -502,10 +502,35 @@ def command(
502502 if op is None or op_ret == op :
503503 return val , data
504504 if byte (data , 0 ) != 0 and byte (data , 1 ) == self .ROM_INVALID_RECV_MSG :
505- # Unsupported read_reg can result in
505+
506+ def drain_input_buffer (buffering_time = 0.2 ):
507+ """
508+ Actively drain the input buffer by reading data
509+ for a specified time. Simple approach for some
510+ drivers that have issues with the buffer flushing.
511+
512+ Args:
513+ buffering_time: Time in seconds to wait for
514+ the buffer to fill.
515+ """
516+ time .sleep (buffering_time )
517+ original_timeout = self ._port .timeout
518+ # Set a very short timeout for draining
519+ self ._port .timeout = 0.001
520+
521+ # Unsupported command response is sent 8 times and has
522+ # 14 bytes length including delimiter 0xC0 bytes.
523+ # At least part of it is read as a command response,
524+ # but to be safe, read it all.
525+ self ._port .read (14 * 8 )
526+
527+ # Restore original timeout
528+ self ._port .timeout = original_timeout
529+ self .flush_input ()
530+
531+ # Unsupported command can result in
506532 # more than one error response for some reason
507- time .sleep (0.2 ) # Wait for input buffer to fill
508- self .flush_input () # Flush input buffer of hanging response
533+ drain_input_buffer (0.2 )
509534 raise UnsupportedCommandError (self , op )
510535
511536 finally :
@@ -1259,6 +1284,10 @@ def run_stub(self, stub: StubFlasher | None = None) -> "ESPLoader":
12591284 log .print ("Stub flasher is already running. No upload is necessary." )
12601285 return self .STUB_CLASS (self ) if self .STUB_CLASS is not None else self
12611286
1287+ secure_boot_workflow = (
1288+ self .CHIP_NAME == "ESP32-S3" and self .get_secure_boot_enabled ()
1289+ )
1290+
12621291 # Upload
12631292 log .print ("Uploading stub flasher..." )
12641293 for field in [stub .text , stub .data ]:
@@ -1271,8 +1300,24 @@ def run_stub(self, stub: StubFlasher | None = None) -> "ESPLoader":
12711300 from_offs = seq * self .ESP_RAM_BLOCK
12721301 to_offs = from_offs + self .ESP_RAM_BLOCK
12731302 self .mem_block (field [from_offs :to_offs ], seq )
1303+
12741304 log .print ("Running stub flasher..." )
1275- self .mem_finish (stub .entry )
1305+ if not secure_boot_workflow :
1306+ self .mem_finish (stub .entry )
1307+ else :
1308+ # Bug in ESP32-S3 ROM prevents stub execution if secure boot is enabled
1309+ # Hijack the `read` function in ROM to point to the stub entrypoint
1310+ # got with GDB - p &rom_spiflash_legacy_funcs.read
1311+ rom_spiflash_legacy_funcs_read_ptr = 0x3FCEF688
1312+ self .mem_finish (0 ) # Finish uploading to RAM but don't run the stub yet
1313+ stored_read_pointer = self .read_reg (rom_spiflash_legacy_funcs_read_ptr )
1314+ self .write_reg (rom_spiflash_legacy_funcs_read_ptr , stub .entry )
1315+ self .command ( # Trigger the `read` in ROM to jump to the stub entrypoint
1316+ self .ESP_CMDS ["READ_FLASH_SLOW" ],
1317+ struct .pack ("<II" , 0 , 0 ),
1318+ wait_response = False ,
1319+ )
1320+
12761321 try :
12771322 p = self .read ()
12781323 except StopIteration :
@@ -1284,6 +1329,9 @@ def run_stub(self, stub: StubFlasher | None = None) -> "ESPLoader":
12841329
12851330 if p != b"OHAI" :
12861331 raise FatalError (f"Failed to start stub flasher. Unexpected response: { p } " )
1332+ if secure_boot_workflow :
1333+ # Restore the original `read` function pointer
1334+ self .write_reg (rom_spiflash_legacy_funcs_read_ptr , stored_read_pointer )
12871335 log .stage (finish = True )
12881336 log .print ("Stub flasher running." )
12891337 return self .STUB_CLASS (self ) if self .STUB_CLASS is not None else self
0 commit comments