@@ -153,6 +153,13 @@ static void handle_read(const uint8_t *data, uint32_t len) {
153153 for (uint32_t j = 0 ; j < 4 && (i + j ) < chunk ; j ++ )
154154 pkt [2 + i + j ] = (val >> ((byte_off + j ) * 8 )) & 0xFF ;
155155 }
156+ } else if (flash_readable && addr >= FLASH_MEM &&
157+ (addr + size ) <= (FLASH_MEM + flash_info .size )) {
158+ /* Register-based flash read — boot mode window wraps at 1MB */
159+ uint8_t tmp [MAX_PAYLOAD ];
160+ flash_read (addr - FLASH_MEM + offset , tmp , chunk );
161+ for (uint32_t i = 0 ; i < chunk ; i ++ )
162+ pkt [2 + i ] = tmp [i ];
156163 } else {
157164 const uint8_t * ptr = (const uint8_t * )addr ;
158165 for (uint32_t i = 0 ; i < chunk ; i ++ )
@@ -177,8 +184,16 @@ static void handle_crc32_cmd(const uint8_t *data, uint32_t len) {
177184 return ;
178185 }
179186
180- const uint8_t * ptr = (const uint8_t * )addr ;
181- uint32_t c = crc32 (0 , ptr , size );
187+ uint32_t c ;
188+ /* Route flash reads through register-based path — boot mode memory
189+ * window wraps at 1MB on some SoCs. */
190+ if (flash_readable && addr >= FLASH_MEM &&
191+ (addr + size ) <= (FLASH_MEM + flash_info .size )) {
192+ c = flash_crc32 (addr - FLASH_MEM , size );
193+ } else {
194+ const uint8_t * ptr = (const uint8_t * )addr ;
195+ c = crc32 (0 , ptr , size );
196+ }
182197 uint8_t resp [4 ];
183198 write_le32 (resp , c );
184199 proto_send (RSP_CRC32 , resp , 4 );
@@ -474,6 +489,129 @@ static void handle_flash_program(const uint8_t *data, uint32_t len) {
474489 proto_send_ack (ACK_OK );
475490}
476491
492+ /*
493+ * CMD_FLASH_STREAM: stream data from UART directly to flash.
494+ *
495+ * Processes one sector at a time: erase → receive → program.
496+ * No separate RAM upload phase — data flows UART → RAM buffer → flash.
497+ * Host streams DATA packets continuously; agent sends per-sector progress.
498+ *
499+ * Host sends: CMD_FLASH_STREAM [flash_addr:4LE] [size:4LE] [crc:4LE]
500+ * Agent ACKs, then for each sector: erase, receive 64KB, program pages.
501+ * Progress: RSP_DATA [sector_done:2LE] [total:2LE] after each sector.
502+ * Final: ACK_OK or ACK_CRC_ERROR.
503+ */
504+ static void handle_flash_stream (const uint8_t * data , uint32_t len ) {
505+ if (len < 12 ) { proto_send_ack (ACK_CRC_ERROR ); return ; }
506+ if (!flash_readable ) { proto_send_ack (ACK_FLASH_ERROR ); return ; }
507+
508+ uint32_t flash_addr = read_le32 (& data [0 ]);
509+ uint32_t size = read_le32 (& data [4 ]);
510+ uint32_t expected_crc = read_le32 (& data [8 ]);
511+
512+ if (size == 0 || flash_addr + size > flash_info .size ) {
513+ proto_send_ack (ACK_FLASH_ERROR );
514+ return ;
515+ }
516+
517+ uint32_t sector_sz = flash_info .sector_size ;
518+ uint32_t page_sz = flash_info .page_size ;
519+ uint32_t num_sectors = (size + sector_sz - 1 ) / sector_sz ;
520+
521+ /* Double buffer: receive into one while erasing+programming the other.
522+ * Only 128KB total — works on all SoCs. */
523+ uint8_t * buf [2 ] = {
524+ (uint8_t * )(RAM_BASE + 0x200000 ),
525+ (uint8_t * )(RAM_BASE + 0x210000 ),
526+ };
527+ int rx_buf = 0 ; /* Buffer currently being filled */
528+
529+ proto_send_ack (ACK_OK );
530+
531+ uint32_t total_received = 0 ;
532+ /* Pending sector to erase+program (from previous iteration) */
533+ int pending_buf = -1 ;
534+ uint32_t pending_addr = 0 ;
535+ uint32_t pending_bytes = 0 ;
536+
537+ for (uint32_t s = 0 ; s < num_sectors ; s ++ ) {
538+ uint32_t sector_offset = s * sector_sz ;
539+ uint32_t sector_bytes = size - sector_offset ;
540+ if (sector_bytes > sector_sz ) sector_bytes = sector_sz ;
541+
542+ /* Receive sector data into rx_buf.
543+ * No flash operations during receive — UART stays responsive. */
544+ uint32_t buf_received = 0 ;
545+ uint8_t pkt [MAX_PAYLOAD + 16 ];
546+ while (buf_received < sector_bytes ) {
547+ uint32_t pkt_len = 0 ;
548+ uint8_t cmd = proto_recv (pkt , & pkt_len , 10000 );
549+ if (cmd == RSP_DATA && pkt_len > 2 ) {
550+ uint32_t chunk = pkt_len - 2 ;
551+ for (uint32_t i = 0 ; i < chunk && buf_received < sector_bytes ; i ++ )
552+ buf [rx_buf ][buf_received ++ ] = pkt [2 + i ];
553+ } else if (cmd == 0 ) {
554+ uint8_t err [5 ];
555+ err [0 ] = ACK_FLASH_ERROR ;
556+ write_le32 (& err [1 ], total_received + buf_received );
557+ proto_send (RSP_ACK , err , 5 );
558+ return ;
559+ }
560+ }
561+
562+ total_received += sector_bytes ;
563+
564+ /* Tell host: "sector received, send next now!"
565+ * Host starts streaming next sector immediately. */
566+ {
567+ uint8_t progress [4 ];
568+ progress [0 ] = ((s + 1 ) >> 0 ) & 0xFF ;
569+ progress [1 ] = ((s + 1 ) >> 8 ) & 0xFF ;
570+ progress [2 ] = (num_sectors >> 0 ) & 0xFF ;
571+ progress [3 ] = (num_sectors >> 8 ) & 0xFF ;
572+ proto_send (RSP_DATA , progress , 4 );
573+ }
574+
575+ /* Process previous sector if pending (erase + program).
576+ * Host is streaming next sector into the OTHER buffer right now. */
577+ if (pending_buf >= 0 ) {
578+ flash_erase_sector (pending_addr );
579+ uint32_t offset = 0 ;
580+ while (offset < pending_bytes ) {
581+ uint32_t chunk = pending_bytes - offset ;
582+ if (chunk > page_sz ) chunk = page_sz ;
583+ flash_write_page (pending_addr + offset ,
584+ & buf [pending_buf ][offset ], chunk );
585+ offset += chunk ;
586+ proto_drain_fifo ();
587+ }
588+ }
589+
590+ /* This sector's buffer becomes the pending one */
591+ pending_buf = rx_buf ;
592+ pending_addr = flash_addr + sector_offset ;
593+ pending_bytes = sector_bytes ;
594+
595+ /* Swap to the other buffer for next receive */
596+ rx_buf ^= 1 ;
597+ }
598+
599+ /* Process the last sector (no more data to receive) */
600+ if (pending_buf >= 0 ) {
601+ flash_erase_sector (pending_addr );
602+ uint32_t offset = 0 ;
603+ while (offset < pending_bytes ) {
604+ uint32_t chunk = pending_bytes - offset ;
605+ if (chunk > page_sz ) chunk = page_sz ;
606+ flash_write_page (pending_addr + offset ,
607+ & buf [pending_buf ][offset ], chunk );
608+ offset += chunk ;
609+ }
610+ }
611+
612+ proto_send_ack (ACK_OK );
613+ }
614+
477615/*
478616 * ARM32 position-independent trampoline (machine code).
479617 * Copies r2 bytes from r1 to r0, then branches to r0-r2 (original dst).
@@ -790,6 +928,9 @@ int main(void) {
790928 case CMD_FLASH_PROGRAM :
791929 handle_flash_program (cmd_buf , data_len );
792930 break ;
931+ case CMD_FLASH_STREAM :
932+ handle_flash_stream (cmd_buf , data_len );
933+ break ;
793934 case CMD_SET_BAUD :
794935 handle_set_baud (cmd_buf , data_len );
795936 break ;
0 commit comments