@@ -502,10 +502,35 @@ def command(
502
502
if op is None or op_ret == op :
503
503
return val , data
504
504
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
506
532
# 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 )
509
534
raise UnsupportedCommandError (self , op )
510
535
511
536
finally :
@@ -1259,6 +1284,10 @@ def run_stub(self, stub: StubFlasher | None = None) -> "ESPLoader":
1259
1284
log .print ("Stub flasher is already running. No upload is necessary." )
1260
1285
return self .STUB_CLASS (self ) if self .STUB_CLASS is not None else self
1261
1286
1287
+ secure_boot_workflow = (
1288
+ self .CHIP_NAME == "ESP32-S3" and self .get_secure_boot_enabled ()
1289
+ )
1290
+
1262
1291
# Upload
1263
1292
log .print ("Uploading stub flasher..." )
1264
1293
for field in [stub .text , stub .data ]:
@@ -1271,8 +1300,24 @@ def run_stub(self, stub: StubFlasher | None = None) -> "ESPLoader":
1271
1300
from_offs = seq * self .ESP_RAM_BLOCK
1272
1301
to_offs = from_offs + self .ESP_RAM_BLOCK
1273
1302
self .mem_block (field [from_offs :to_offs ], seq )
1303
+
1274
1304
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
+
1276
1321
try :
1277
1322
p = self .read ()
1278
1323
except StopIteration :
@@ -1284,6 +1329,9 @@ def run_stub(self, stub: StubFlasher | None = None) -> "ESPLoader":
1284
1329
1285
1330
if p != b"OHAI" :
1286
1331
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 )
1287
1335
log .stage (finish = True )
1288
1336
log .print ("Stub flasher running." )
1289
1337
return self .STUB_CLASS (self ) if self .STUB_CLASS is not None else self
0 commit comments