mcu_hi3321_watch/middleware/utils/codeloader/private/codeloader.c
2025-05-26 20:15:20 +08:00

379 lines
15 KiB
C

/*
* Copyright (c) CompanyNameMagicTag 2018-2020. All rights reserved.
* Description: BT Codeloader Module
*/
#include <stdint.h>
#include <stdbool.h>
#include "non_os.h"
#include "codeloader.h"
#include "tcxo.h"
#include "hal_uart.h"
#include "hal_reboot.h"
#include "flash.h"
#include "flash_porting.h"
#include "flash_common_config.h"
#include "codeloader_common.h"
#include "codeloader_uart.h"
#include "codeloader_message_layer.h"
#include "codeloader_command_layer.h"
#include "codeloader_line_layer.h"
#include "securec.h"
#include "codeloader_command_processor_utils.h"
#include "codeloader_locking.h"
#include "codeloader_execute_command_control_commands.h"
#ifdef SUPPORT_PARTITION_INFO
#include "partition.h"
#endif
#if defined(BUILD_APPLICATION_SSB) || defined(BUILD_APPLICATION_ROM)
#include "watchdog.h"
#endif
uint16_t flash_config_get_kv_pages(void);
/*
* Configuration parameters
*/
/** Milliseconds to wait for connection after the init message has been sent */
#define CODELOADER_SHORT_WAIT_FOR_CONNECTION_MS 200UL // !< 200 milliseconds
#define CODELOADER_LONG_WAIT_FOR_CONNECTION_MS 600000UL // !< 10 minutes
/** Milliseconds between successive code loader init messages issued over UART */
#define CODELOADER_INIT_MESSAGE_REPEAT_MS 18000UL
/** Maximum receiver buffer size for the codeloader */
#ifdef LARGE_BUFFER
#define CODELOADER_MAX_RX_BUFFER_SIZE ((5 * FLASH_PAGE_SIZE) + 256)
#else
#define CODELOADER_MAX_RX_BUFFER_SIZE ((2 * FLASH_PAGE_SIZE) + 256)
#endif
/*
* Private definitions
*/
/** Size of the line, layer and message headers before the parameters */
#define CODELOADER_MAIN_HEADER_SIZE_BEFORE_PARAMETERS 2
/** Buffer offset that prevents miss-alignment of the data to write to the flash */
#define CODELOADER_MAIN_BUFFER_OFFSET (4 - CODELOADER_MAIN_HEADER_SIZE_BEFORE_PARAMETERS % 4)
/** Codeloader UART Internal Reception buffer size (bytes) */
#define CODELOADER_UART_RX_BUFFER_SIZE (CODELOADER_MAX_RX_BUFFER_SIZE - CODELOADER_MAIN_BUFFER_OFFSET)
//lint -esym(754, codeloader_rx_buffer_container_struct::paddding_bytes) Padding
struct __attribute__((__packed__)) codeloader_rx_buffer_container_struct {
uint8_t paddding_bytes[CODELOADER_MAIN_BUFFER_OFFSET];
uint8_t codeloader_reception_buffer[CODELOADER_MAX_RX_BUFFER_SIZE];
} codeloader_rx_buffer_container __attribute__((aligned(4)));
/** Codeloader module state vector */
static volatile codeloader_state_e g_codeloader_main_state;
/** Codeloader UART Internal Reception buffer for initialization */
static codeloader_rx_buffer_t g_codeloader_main_rx_buffer;
/** Codeloader UART Internal Reception buffer to process */
static codeloader_rx_buffer_t g_codeloader_main_received_buffer_to_process;
/** flag to indicate that there is data to process */
static volatile bool g_codeloader_main_there_is_data_to_process;
/** Pointer to the codeloader reception buffer */
//lint -esym(843, g_codeloader_reception_buffer) Don't whine about var could be constant.
static uint8_t *g_codeloader_reception_buffer = codeloader_rx_buffer_container.codeloader_reception_buffer;
/** Indicates if Codeloader has updated the security core image */
static bool g_codeloader_security_core_updated = false;
/**
* Erases the supplied codeloader rx buffer
* @param codeloader_buffer Pointer to codeloader rx buffer to be erased
*/
static void codeloader_erase_buffer(const codeloader_rx_buffer_t *codeloader_buffer);
/**
* Sends the welcome message
*/
static void codeloader_main_send_init_message(const codeloader_options_t *options);
/**
* Get function for the data process internal flag.
* @return true if there is valid data to process that should be given to the data handler, false otherwise.
*/
static bool codeloader_main_is_there_data_to_process(void);
/**
* Takes care of the received data and generates a reply internally.
* @param received_buffer pointer to the received buffer
*/
static void codeloader_main_rx_data_handler(const codeloader_rx_buffer_t *received_buffer);
/**
* Callback being invoked when a valid frame has arrived to the uart or an error has ocurred.
* @param data pointer to the data buffer
* @param cause Reason for the callback
*/
static void codeloader_main_rx_data_callback(const codeloader_rx_buffer_t *data, codeloader_callback_cause_e cause);
/**
* Callback being invoked when the transmission of the reply has been completed.
*/
static void codeloader_main_tx_data_callback(void);
/* Initialize the codeloader resources */
void codeloader_init(const codeloader_options_t *options)
{
g_codeloader_main_rx_buffer.buffer = g_codeloader_reception_buffer;
g_codeloader_main_rx_buffer.length = CODELOADER_UART_RX_BUFFER_SIZE;
g_codeloader_main_state = CODELOADER_STATE_WAITING_FOR_INIT;
g_codeloader_security_core_updated = false;
codeloader_lock_init(options);
codeloader_uart_default_init(&g_codeloader_main_rx_buffer, codeloader_main_rx_data_callback,
codeloader_main_tx_data_callback);
}
/* De-initialize the Codeloader resources */
void codeloader_deinit(void)
{
codeloader_uart_deinit();
codeloader_erase_buffer(&g_codeloader_main_rx_buffer);
}
/*
* Erases the supplied codeloader rx buffer
*/
static void codeloader_erase_buffer(const codeloader_rx_buffer_t *codeloader_buffer)
{
if ((codeloader_buffer != NULL) && (codeloader_buffer->buffer != NULL)) {
memset_s(codeloader_buffer->buffer, codeloader_buffer->length, 0, codeloader_buffer->length);
}
}
/* Main loop to run while the Codeloader is active */
bool codeloader_main_loop(const codeloader_options_t *options, codeloader_retp_t *cl_retp)
{
bool codeloader_executed = false; // true if it passes the init message
cl_retp->running_from_bootloader_ram = false;
codeloader_init(options); // Initialise the needed modules and internal state
// TCXO time when the codeloader considers it has timed out
uint64_t start_time = uapi_tcxo_get_ms();
uint64_t time_out = CODELOADER_SHORT_WAIT_FOR_CONNECTION_MS;
if (options->use_long_timeout) { // Flash is *not* valid, so wait for an extended amount of time in the codeloader
time_out = CODELOADER_LONG_WAIT_FOR_CONNECTION_MS;
}
// Will cause an immediate send of the first codeloader init message
uint32_t last_init_message_period = (uint32_t)-1;
while ((codeloader_main_get_state() == CODELOADER_STATE_WAITING_FOR_INIT) &&
((uapi_tcxo_get_ms() - start_time) < time_out)) {
uint32_t init_message_period = (uint32_t)((uapi_tcxo_get_ms() - start_time) /
CODELOADER_INIT_MESSAGE_REPEAT_MS);
if (init_message_period != last_init_message_period) { // Issue codeloader init message
// Should not be re-issued if codeloader has moved out of its WAITING_FOR_INIT state
last_init_message_period = init_message_period;
codeloader_main_send_init_message(options);
}
if (codeloader_main_is_there_data_to_process()) {
codeloader_main_rx_data_handler(&g_codeloader_main_received_buffer_to_process);
}
#if defined(BUILD_APPLICATION_SSB) || defined(BUILD_APPLICATION_ROM)
// kick the watch dog, we could be here some time
uapi_watchdog_kick();
#endif
}
// if we are in waiting for init state we have timedout
if (codeloader_main_get_state() != CODELOADER_STATE_WAITING_FOR_INIT) {
codeloader_executed = true;
// while we don't receive the exit code we keep running the codeloader
while (codeloader_main_get_state() != CODELOADER_STATE_EXIT) {
if (codeloader_main_is_there_data_to_process()) {
codeloader_main_rx_data_handler(&g_codeloader_main_received_buffer_to_process);
}
#if defined(BUILD_APPLICATION_SSB) || defined(BUILD_APPLICATION_ROM)
// kick the watch dog, we could be here some time
uapi_watchdog_kick();
#endif
}
}
codeloader_deinit();
cl_retp->running_from_bootloader_ram = codeloader_command_processor_is_running_from_bootloader_ram();
cl_retp->security_core_updated = g_codeloader_security_core_updated;
return codeloader_executed;
}
void codeloader_main_set_state(codeloader_state_e state)
{
non_os_enter_critical();
g_codeloader_main_state = state;
non_os_exit_critical();
}
codeloader_state_e codeloader_main_get_state(void)
{
return g_codeloader_main_state;
}
/*
* Log that Codeloader has updated the security core image
*/
void codeloader_set_security_core_updated(void)
{
g_codeloader_security_core_updated = true;
}
static void codeloader_main_send_init_message(const codeloader_options_t *options)
{
codeloader_message_layer_send_init_message(options);
}
static bool codeloader_main_is_there_data_to_process(void)
{
return g_codeloader_main_there_is_data_to_process;
}
static void codeloader_line_layer_succ_action(codeloader_rx_buffer_t *processing_buffer,
codeloader_common_generic_buffer_t *output_buffer,
codeloader_message_layer_nack_message_code_e *nack_message,
bool *send_message)
{
codeloader_message_layer_return_e message_layer_return_value;
codeloader_command_layer_return_e codeloader_layer_return_value;
codeloader_common_generic_buffer_t input_buffer;
message_layer_return_value = codeloader_message_layer_process_input_message(processing_buffer);
if (message_layer_return_value == CODELOADER_MESSAGE_LAYER_RETURN_SUCCESS) {
control_command_uart_message_recieved_set(true); // Used as a flag for the change uart speed command
input_buffer.buffer = processing_buffer->buffer;
input_buffer.length = (uint16_t)processing_buffer->length;
codeloader_layer_return_value = codeloader_command_layer_process_input_message(processing_buffer,
output_buffer);
switch (codeloader_layer_return_value) {
case CODELOADER_COMMAND_LAYER_RETURN_SEND_REPLY_IN_OUTPUT_BUFFER:
// only form the reply message if needed
codeloader_message_layer_form_reply(output_buffer, &input_buffer);
break;
case CODELOADER_COMMAND_LAYER_RETURN_DO_NOT_SEND_ANY_REPLY:
*send_message = false;
break;
case CODELOADER_COMMAND_LAYER_RETURN_RESEND_LAST_MESSAGE:
// if no message has been sent just ignore it
*send_message = (codeloader_uart_get_last_message(output_buffer)) ? true : false;
break;
default:
break;
}
} else {
// only reply if the state is not wating for init
if (codeloader_main_get_state() == CODELOADER_STATE_WAITING_FOR_INIT) {
*send_message = false;
} else {
*nack_message = CODELOADER_MESSAGE_LAYER_NACK_MESSAGE_CODE_MESSAGE_BAD_FORMED;
codeloader_message_layer_form_nack_message(output_buffer, *nack_message);
}
}
}
static void codeloader_main_rx_data_handler(const codeloader_rx_buffer_t *received_buffer)
{
codeloader_rx_buffer_t processing_buffer;
codeloader_line_layer_return_e line_layer_return_value;
bool send_message = true;
codeloader_message_layer_nack_message_code_e nack_message;
codeloader_common_generic_buffer_t output_buffer;
processing_buffer.buffer = received_buffer->buffer;
processing_buffer.length = received_buffer->length;
line_layer_return_value = codeloader_line_layer_process_input_message(&processing_buffer);
if (line_layer_return_value == CODELOADER_LINE_LAYER_RETURN_SUCCESS) {
codeloader_line_layer_succ_action(&processing_buffer, &output_buffer, &nack_message, &send_message);
} else {
switch (line_layer_return_value) {
case CODELOADER_LINE_LAYER_RETURN_BAD_CRC:
nack_message = CODELOADER_MESSAGE_LAYER_NACK_MESSAGE_CODE_LINE_BAD_CRC;
break;
case CODELOADER_LINE_LAYER_RETURN_BAD_FORMED_MESSAGE:
nack_message = CODELOADER_MESSAGE_LAYER_NACK_MESSAGE_CODE_LINE_BAD_FORMED;
break;
default:
nack_message = CODELOADER_MESSAGE_LAYER_NACK_GENERIC_ERROR;
break;
}
// only reply if the state is not wating for init
if (codeloader_main_get_state() == CODELOADER_STATE_WAITING_FOR_INIT) {
send_message = false;
} else {
codeloader_message_layer_form_nack_message(&output_buffer, nack_message);
}
}
// only reply if the state is not wating for init
if (send_message) {
codeloader_line_layer_send(&output_buffer);
} else { // listen again
g_codeloader_main_there_is_data_to_process = false;
codeloader_uart_enable_reception();
}
}
//lint -esym(459, codeloader_main_rx_data_callback)
//lint -esym(459, codeloader_main_tx_data_callback)
static void codeloader_main_rx_data_callback(const codeloader_rx_buffer_t *received_buffer,
codeloader_callback_cause_e event)
{
if (event == CODELOADER_CALLBACK_CAUSE_VALID_BUFFER) {
codeloader_uart_disble_reception();
g_codeloader_main_received_buffer_to_process.buffer = received_buffer->buffer;
g_codeloader_main_received_buffer_to_process.length = received_buffer->length;
g_codeloader_main_there_is_data_to_process = true;
}
}
static void codeloader_main_tx_data_callback(void)
{
g_codeloader_main_there_is_data_to_process = false;
if (codeloader_main_get_state() == CODELOADER_STATE_UART_SPEED_CHANGING) {
codeloader_command_processor_set_uart_speed_set();
#ifndef BUILD_APPLICATION_ROM
} else if (codeloader_main_get_state() == CODELOADER_STATE_ERASE_NV_AND_RESET) {
#ifdef SUPPORT_PARTITION_INFO
partition_information_t info;
errcode_t ret_val = uapi_partition_get_info(PARTITION_NV_DATA, &info);
if (ret_val == ERRCODE_SUCC && info.type == PARTITION_BY_ADDRESS) {
uapi_flash_block_erase(CHIP_FLASH_ID, info.part_info.addr_info.addr, info.part_info.addr_info.size, true);
}
#else
uint32_t start_addr = ((flash_config_get_kv_pages() - SYSTEM_RESERVED_FLASH_PAGES) - 1) * FLASH_PAGE_SIZE;
uapi_flash_block_erase(CHIP_FLASH_ID, start_addr, SYSTEM_RESERVED_FLASH_PAGES * FLASH_PAGE_SIZE, true);
#endif
hal_reboot_chip();
} else if (codeloader_main_get_state() == CODELOADER_STATE_ERASE_SSB_AND_RESET) {
codeloader_uart_wait_to_finish_transmission();
memset_s((void *)SSB_BOOT_RAM_ORIGIN, APP_VECTORS_LENGTH, 0, APP_VECTORS_LENGTH);
hal_reboot_chip();
#endif
} else {
codeloader_uart_enable_reception();
}
}
void codeloader_error_log_record(uint8_t cmd_code, int8_t cmd_ret_value)
{
#ifndef BUILD_APPLICATION_ROM
if (cmd_ret_value != CODELOADER_COMMAND_GENERAL_RETURN_CODE_SUCCESS) {
codeloader_log_t error_log;
error_log.command_code = cmd_code;
error_log.command_ret_value = cmd_ret_value;
// write codeloader error log to flash. To do.
UNUSED(error_log);
}
#else
UNUSED(cmd_code);
UNUSED(cmd_ret_value);
#endif
}