379 lines
15 KiB
C
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
|
|
}
|