4444#define RCVR_MAX_AUTH_FAILS 5
4545#define RCVR_BACKOFF_BASE_MS 1000
4646
47+ #define RCVR_LOG_MAX_ENTRIES 8
48+
4749/* Recovery protocol packet header */
4850#ifdef _MSC_VER
4951#pragma pack(push, 1)
@@ -80,6 +82,8 @@ extern int eos_slot_erase(eos_slot_t slot);
8082
8183/* Forward declarations from boot_log */
8284extern void eos_boot_log_append (uint32_t event , uint32_t slot , uint32_t detail );
85+ extern int eos_boot_log_read (uint32_t index , eos_boot_log_entry_t * out );
86+ extern uint32_t eos_boot_log_get_head (void );
8387
8488static int recovery_send_ack (void )
8589{
@@ -96,19 +100,20 @@ static int recovery_send_nack(void)
96100/**
97101 * @brief Check if a command requires authentication.
98102 */
99- static bool cmd_requires_auth (uint8_t cmd )
100- {
101- switch (cmd ) {
102- case RCVR_CMD_ERASE :
103- case RCVR_CMD_WRITE :
104- case RCVR_CMD_VERIFY :
105- case RCVR_CMD_BOOT :
106- case RCVR_CMD_FACTORY :
107- return true;
108- default :
109- return false;
110- }
111- }
103+ static bool cmd_requires_auth (uint8_t cmd )
104+ {
105+ switch (cmd ) {
106+ case RCVR_CMD_ERASE :
107+ case RCVR_CMD_WRITE :
108+ case RCVR_CMD_VERIFY :
109+ case RCVR_CMD_BOOT :
110+ case RCVR_CMD_FACTORY :
111+ case RCVR_CMD_LOG :
112+ return true;
113+ default :
114+ return false;
115+ }
116+ }
112117
113118/**
114119 * @brief Handle authentication challenge-response.
@@ -169,18 +174,18 @@ static int recovery_handle_auth(void)
169174 eos_sha256_init (& ctx );
170175 eos_sha256_update (& ctx , challenge , RCVR_CHALLENGE_SIZE );
171176
172- /* Read shared secret from OTP */
173- uint8_t shared_secret [32 ];
174- rc = eos_hal_otp_read (0x180 , shared_secret , sizeof (shared_secret ));
175- if (rc != EOS_OK ) {
176- /* Fail authentication if OTP secret is unreadable */
177- auth_fail_count ++ ;
178- auth_state = RCVR_AUTH_NONE ;
179- eos_boot_log_append (0x21 , EOS_SLOT_NONE , auth_fail_count ); /* AUTH_FAIL */
180- return recovery_send_nack ();
181- }
182-
183- eos_sha256_update (& ctx , shared_secret , sizeof (shared_secret ));
177+ /* Read shared secret from OTP */
178+ uint8_t shared_secret [32 ];
179+ rc = eos_hal_otp_read (0x180 , shared_secret , sizeof (shared_secret ));
180+ if (rc != EOS_OK ) {
181+ /* Fail authentication if OTP secret is unreadable */
182+ auth_fail_count ++ ;
183+ auth_state = RCVR_AUTH_NONE ;
184+ eos_boot_log_append (0x21 , EOS_SLOT_NONE , auth_fail_count ); /* AUTH_FAIL */
185+ return recovery_send_nack ();
186+ }
187+
188+ eos_sha256_update (& ctx , shared_secret , sizeof (shared_secret ));
184189 eos_sha256_final (& ctx , expected );
185190
186191 /* Securely zero the secret */
@@ -303,16 +308,16 @@ static int recovery_handle_verify(eos_slot_t slot)
303308 if (rc != EOS_OK )
304309 return recovery_send_nack ();
305310
306- /* eos_image_verify_integrity() adds hdr_size internally — pass base addr only */
307- rc = eos_image_verify_integrity (& hdr , addr );
308- if (rc != EOS_OK )
309- return recovery_send_nack ();
310-
311- rc = eos_image_verify_signature (& hdr );
312- if (rc != EOS_OK )
313- return recovery_send_nack ();
314-
315- return recovery_send_ack ();
311+ /* eos_image_verify_integrity() adds hdr_size internally — pass base addr only */
312+ rc = eos_image_verify_integrity (& hdr , addr );
313+ if (rc != EOS_OK )
314+ return recovery_send_nack ();
315+
316+ rc = eos_image_verify_signature (& hdr );
317+ if (rc != EOS_OK )
318+ return recovery_send_nack ();
319+
320+ return recovery_send_ack ();
316321}
317322
318323static int recovery_handle_boot (eos_slot_t slot , eos_bootctl_t * bctl )
@@ -335,19 +340,98 @@ static int recovery_handle_boot(eos_slot_t slot, eos_bootctl_t *bctl)
335340 return EOS_OK ;
336341}
337342
338- static int recovery_handle_factory_reset (eos_bootctl_t * bctl )
343+ static int recovery_handle_factory_reset (eos_bootctl_t * bctl )
344+ {
345+ int rc1 = eos_slot_erase (EOS_SLOT_A );
346+ int rc2 = eos_slot_erase (EOS_SLOT_B );
347+ eos_bootctl_init_defaults (bctl );
348+ int rc3 = eos_bootctl_save (bctl );
349+
350+ if (rc1 != EOS_OK || rc2 != EOS_OK || rc3 != EOS_OK ) {
351+ return recovery_send_nack ();
352+ }
353+
354+ eos_boot_log_append (EOS_LOG_FACTORY_RESET , EOS_SLOT_NONE , 0 );
355+ return recovery_send_ack ();
356+ }
357+
358+ static int recovery_collect_boot_log_entries (
359+ eos_boot_log_entry_t * entries_out ,
360+ uint16_t * entry_count_out
361+ )
339362{
340- int rc1 = eos_slot_erase (EOS_SLOT_A );
341- int rc2 = eos_slot_erase (EOS_SLOT_B );
342- eos_bootctl_init_defaults (bctl );
343- int rc3 = eos_bootctl_save (bctl );
344-
345- if (rc1 != EOS_OK || rc2 != EOS_OK || rc3 != EOS_OK ) {
363+ eos_boot_log_entry_t raw_entries [EOS_BOOT_LOG_MAX ];
364+ uint32_t valid_entry_count = 0 ;
365+ uint32_t log_head ;
366+ uint16_t written_count = 0 ;
367+
368+ if (!entries_out || !entry_count_out )
369+ return EOS_ERR_INVALID ;
370+
371+ for (uint32_t i = 0 ; i < EOS_BOOT_LOG_MAX ; i ++ ) {
372+ int rc = eos_boot_log_read (i , & raw_entries [i ]);
373+ if (rc != EOS_OK )
374+ return rc ;
375+
376+ if (raw_entries [i ].event != 0 )
377+ valid_entry_count ++ ;
378+ }
379+
380+ log_head = eos_boot_log_get_head () % EOS_BOOT_LOG_MAX ;
381+
382+ for (uint32_t i = 0 ; i < valid_entry_count ; i ++ ) {
383+ uint32_t entry_index = (valid_entry_count == EOS_BOOT_LOG_MAX ) ?
384+ ((log_head + i ) % EOS_BOOT_LOG_MAX ) : i ;
385+
386+ if (raw_entries [entry_index ].event == 0 )
387+ continue ;
388+
389+ entries_out [written_count ++ ] = raw_entries [entry_index ];
390+ }
391+
392+ * entry_count_out = written_count ;
393+ return EOS_OK ;
394+ }
395+
396+ static int recovery_handle_boot_log (uint32_t start_index , uint16_t requested_count )
397+ {
398+ eos_boot_log_entry_t boot_log_entries [EOS_BOOT_LOG_MAX ];
399+ uint16_t total_entries = 0 ;
400+ uint16_t response_entry_count ;
401+ int rc ;
402+
403+ if (requested_count == 0 )
404+ requested_count = RCVR_LOG_MAX_ENTRIES ;
405+ if (requested_count > RCVR_LOG_MAX_ENTRIES )
406+ requested_count = RCVR_LOG_MAX_ENTRIES ;
407+
408+ rc = recovery_collect_boot_log_entries (boot_log_entries , & total_entries );
409+ if (rc != EOS_OK )
346410 return recovery_send_nack ();
411+
412+ if (start_index >= total_entries ) {
413+ uint8_t empty_response_header [3 ] = { RCVR_ACK , 0 , 0 };
414+ return eos_hal_uart_send (empty_response_header , sizeof (empty_response_header ));
347415 }
416+
417+ response_entry_count = (uint16_t )(total_entries - start_index );
418+ if (response_entry_count > requested_count )
419+ response_entry_count = requested_count ;
420+
421+ uint8_t response_header [3 ] = {
422+ RCVR_ACK ,
423+ (uint8_t )(response_entry_count & 0xFF ),
424+ (uint8_t )((response_entry_count >> 8 ) & 0xFF )
425+ };
426+
427+ rc = eos_hal_uart_send (response_header , sizeof (response_header ));
428+ if (rc != EOS_OK )
429+ return rc ;
348430
349- eos_boot_log_append (EOS_LOG_FACTORY_RESET , EOS_SLOT_NONE , 0 );
350- return recovery_send_ack ();
431+ return eos_hal_uart_send (
432+ & boot_log_entries [start_index ],
433+ response_entry_count * sizeof (boot_log_entries [0 ])
434+ );
351435}
352436
353437int eos_recovery_enter (eos_bootctl_t * bctl )
@@ -415,6 +499,10 @@ int eos_recovery_enter(eos_bootctl_t *bctl)
415499 recovery_handle_factory_reset (bctl );
416500 break ;
417501
502+ case RCVR_CMD_LOG :
503+ recovery_handle_boot_log (pkt .offset , pkt .len );
504+ break ;
505+
418506 default :
419507 recovery_send_nack ();
420508 break ;
0 commit comments