@@ -380,6 +380,102 @@ static void handle_flash_write(const uint8_t *data, uint32_t len) {
380380 proto_send_ack (ACK_OK );
381381}
382382
383+ /*
384+ * CMD_FLASH_PROGRAM: erase + program flash from RAM.
385+ *
386+ * The U-Boot approach: host writes data to RAM first (via CMD_WRITE),
387+ * then sends this command to erase + program flash from that RAM buffer.
388+ * Agent does the entire flash operation locally, sending per-sector
389+ * progress so the host knows it's alive.
390+ *
391+ * Host sends: CMD_FLASH_PROGRAM [ram_addr:4LE] [flash_addr:4LE]
392+ * [size:4LE] [expected_crc:4LE]
393+ * Agent: verifies RAM CRC → erases sectors → programs pages
394+ * Progress: RSP_DATA [sectors_done:2LE] [total_sectors:2LE] per sector
395+ * Final: ACK_OK or ACK_CRC_ERROR/ACK_FLASH_ERROR
396+ */
397+ static void handle_flash_program (const uint8_t * data , uint32_t len ) {
398+ if (len < 16 ) { proto_send_ack (ACK_CRC_ERROR ); return ; }
399+ if (!flash_readable ) { proto_send_ack (ACK_FLASH_ERROR ); return ; }
400+
401+ uint32_t ram_addr = read_le32 (& data [0 ]);
402+ uint32_t flash_addr = read_le32 (& data [4 ]);
403+ uint32_t size = read_le32 (& data [8 ]);
404+ uint32_t expected_crc = read_le32 (& data [12 ]);
405+
406+ /* Validate */
407+ if (size == 0 || flash_addr + size > flash_info .size ) {
408+ proto_send_ack (ACK_FLASH_ERROR );
409+ return ;
410+ }
411+ if (!addr_readable (ram_addr , size )) {
412+ proto_send_ack (ACK_FLASH_ERROR );
413+ return ;
414+ }
415+
416+ const uint8_t * src = (const uint8_t * )ram_addr ;
417+
418+ /* Verify RAM data CRC before touching flash */
419+ uint32_t actual_crc = crc32 (0 , src , size );
420+ if (actual_crc != expected_crc ) {
421+ uint8_t err [9 ];
422+ err [0 ] = ACK_CRC_ERROR ;
423+ write_le32 (& err [1 ], actual_crc );
424+ write_le32 (& err [5 ], size );
425+ proto_send (RSP_ACK , err , 9 );
426+ return ;
427+ }
428+
429+ proto_send_ack (ACK_OK ); /* CRC verified, starting flash operation */
430+
431+ uint32_t sector_sz = flash_info .sector_size ;
432+ uint32_t page_sz = flash_info .page_size ;
433+
434+ /* Round up to sector boundary for erase */
435+ uint32_t erase_start = flash_addr & ~(sector_sz - 1 );
436+ uint32_t erase_end = (flash_addr + size + sector_sz - 1 ) & ~(sector_sz - 1 );
437+ uint32_t num_sectors = (erase_end - erase_start ) / sector_sz ;
438+ uint32_t total_sectors = num_sectors ;
439+
440+ /* Phase 1: Erase sectors */
441+ for (uint32_t s = 0 ; s < num_sectors ; s ++ ) {
442+ flash_erase_sector (erase_start + s * sector_sz );
443+
444+ /* Progress: [sectors_done:2LE] [total:2LE] */
445+ uint8_t progress [4 ];
446+ progress [0 ] = ((s + 1 ) >> 0 ) & 0xFF ;
447+ progress [1 ] = ((s + 1 ) >> 8 ) & 0xFF ;
448+ progress [2 ] = (total_sectors >> 0 ) & 0xFF ;
449+ progress [3 ] = (total_sectors >> 8 ) & 0xFF ;
450+ proto_send (RSP_DATA , progress , 4 );
451+ }
452+
453+ /* Phase 2: Program pages */
454+ uint32_t offset = 0 ;
455+ while (offset < size ) {
456+ uint32_t chunk = size - offset ;
457+ if (chunk > page_sz ) chunk = page_sz ;
458+ flash_write_page (flash_addr + offset , & src [offset ], chunk );
459+ offset += chunk ;
460+
461+ /* Progress every 64 pages (16KB) to keep host alive */
462+ if ((offset % (page_sz * 64 )) == 0 || offset >= size ) {
463+ uint8_t progress [4 ];
464+ uint16_t done = (uint16_t )(total_sectors + offset / (page_sz * 64 ));
465+ uint16_t total = (uint16_t )(total_sectors + size / (page_sz * 64 ));
466+ progress [0 ] = (done >> 0 ) & 0xFF ;
467+ progress [1 ] = (done >> 8 ) & 0xFF ;
468+ progress [2 ] = (total >> 0 ) & 0xFF ;
469+ progress [3 ] = (total >> 8 ) & 0xFF ;
470+ proto_send (RSP_DATA , progress , 4 );
471+ }
472+ }
473+
474+ /* Skip in-agent verify — the FMC memory window may have stale data
475+ * after bulk programming (65536 soft resets). Host verifies separately. */
476+ proto_send_ack (ACK_OK );
477+ }
478+
383479/*
384480 * ARM32 position-independent trampoline (machine code).
385481 * Copies r2 bytes from r1 to r0, then branches to r0-r2 (original dst).
@@ -694,14 +790,20 @@ int main(void) {
694790 case CMD_SCAN :
695791 handle_scan (cmd_buf , data_len );
696792 break ;
793+ case CMD_FLASH_PROGRAM :
794+ handle_flash_program (cmd_buf , data_len );
795+ break ;
697796 case CMD_SET_BAUD :
698797 handle_set_baud (cmd_buf , data_len );
699798 break ;
700799 case CMD_REBOOT :
701- /* Rejected — watchdog reset re-enters bootrom on serial
702- * boot pin, killing the agent with no recovery. Use
703- * SELFUPDATE to reload, or physical power cycle. */
704- proto_send_ack (ACK_FLASH_ERROR );
800+ /* ACK first, then system reset via sysctrl register.
801+ * This is the standard HiSilicon reset (same as Linux
802+ * hisi-reboot driver): write 0xdeadbeef to 0x12020004. */
803+ proto_send_ack (ACK_OK );
804+ for (volatile int i = 0 ; i < 100000 ; i ++ ) {}
805+ * (volatile uint32_t * )0x12020004 = 0xdeadbeef ;
806+ while (1 ) {}
705807 break ;
706808 default :
707809 proto_send_ack (ACK_CRC_ERROR );
0 commit comments