[FL-2269] Core2 OTA (#1144)

* C2OTA: wip
* Update Cube to 1.13.3
* Fixed prio
* Functional Core2 updater
* Removed hardware CRC usage; code cleanup & linter fixes
* Moved hardcoded stack params to copro.mk
* Fixing CI bundling of core2 fw
* Removed last traces of hardcoded radio stack
* OB processing draft
* Python scripts cleanup
* Support for comments in ob data
* Sacrificed SD card icon in favor of faster update. Waiting for Storage fix
* Additional handling for OB mismatched values
* Description for new furi_hal apis; spelling fixes
* Rework of OB write, WIP
* Properly restarting OB verification loop
* Split update_task_workers.c
* Checking OBs after enabling post-update mode
* Moved OB verification before flashing
* Removed ob.data for custom stacks
* Fixed progress calculation for OB
* Removed unnecessary OB mask cast

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
hedger
2022-04-27 18:53:48 +03:00
committed by GitHub
parent 81aeda86db
commit 7ce305fca3
41 changed files with 1622 additions and 295 deletions

View File

@@ -55,7 +55,6 @@ void furi_hal_init() {
FURI_LOG_I(TAG, "Speaker OK");
furi_hal_crypto_init();
furi_hal_crc_init(true);
// USB
#ifndef FURI_RAM_EXEC

View File

@@ -16,6 +16,9 @@
#define FURI_HAL_BT_DEFAULT_MAC_ADDR \
{ 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 }
/* Time, in ms, to wait for mode transition before crashing */
#define C2_MODE_SWITCH_TIMEOUT 10000
osMutexId_t furi_hal_bt_core2_mtx = NULL;
static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown;
@@ -99,7 +102,7 @@ void furi_hal_bt_unlock_core2() {
furi_check(osMutexRelease(furi_hal_bt_core2_mtx) == osOK);
}
static bool furi_hal_bt_radio_stack_is_supported(WirelessFwInfo_t* info) {
static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) {
bool supported = false;
if(info->StackType == INFO_STACK_TYPE_BLE_HCI) {
furi_hal_bt_stack = FuriHalBtStackHciLayer;
@@ -128,21 +131,22 @@ bool furi_hal_bt_start_radio_stack() {
}
do {
// Wait until FUS is started or timeout
WirelessFwInfo_t info = {};
if(!ble_glue_wait_for_fus_start(&info)) {
FURI_LOG_E(TAG, "FUS start failed");
// Wait until C2 is started or timeout
if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) {
FURI_LOG_E(TAG, "Core2 start failed");
LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
ble_glue_thread_stop();
break;
}
// If FUS is running, start radio stack fw
if(ble_glue_radio_stack_fw_launch_started()) {
// If FUS is running do nothing and wait for system reset
furi_crash("Waiting for FUS to launch radio stack firmware");
// If C2 is running, start radio stack fw
if(!furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack)) {
break;
}
// Check weather we support radio stack
if(!furi_hal_bt_radio_stack_is_supported(&info)) {
// Check whether we support radio stack
const BleGlueC2Info* c2_info = ble_glue_get_c2_info();
if(!furi_hal_bt_radio_stack_is_supported(c2_info)) {
FURI_LOG_E(TAG, "Unsupported radio stack");
// Don't stop SHCI for crypto enclave support
break;
@@ -232,7 +236,7 @@ bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb,
ble_app_thread_stop();
gap_thread_stop();
FURI_LOG_I(TAG, "Reset SHCI");
SHCI_C2_Reinit();
ble_glue_reinit_c2();
osDelay(100);
ble_glue_thread_stop();
FURI_LOG_I(TAG, "Start BT initialization");
@@ -404,3 +408,18 @@ void furi_hal_bt_stop_scan() {
gap_stop_scan();
}
}
bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) {
BleGlueCommandResult fw_start_res = ble_glue_force_c2_mode(mode);
if(fw_start_res == BleGlueCommandResultOK) {
return true;
} else if(fw_start_res == BleGlueCommandResultRestartPending) {
// Do nothing and wait for system reset
osDelay(C2_MODE_SWITCH_TIMEOUT);
furi_crash("Waiting for FUS->radio stack transition");
return true;
}
FURI_LOG_E(TAG, "Failed to switch C2 mode: %d", fw_start_res);
return false;
}

View File

@@ -34,6 +34,7 @@ void furi_hal_crc_init(bool synchronize) {
void furi_hal_crc_reset() {
furi_check(hal_crc_control.state == CRC_State_Ready);
if(hal_crc_control.mtx) {
furi_check(osMutexGetOwner(hal_crc_control.mtx) == osThreadGetId());
osMutexRelease(hal_crc_control.mtx);
}
LL_CRC_ResetCRCCalculationUnit(CRC);
@@ -84,5 +85,9 @@ uint32_t furi_hal_crc_feed(void* data, uint16_t length) {
bool furi_hal_crc_acquire(uint32_t timeout) {
furi_assert(hal_crc_control.mtx);
return osMutexAcquire(hal_crc_control.mtx, timeout) == osOK;
if(osMutexAcquire(hal_crc_control.mtx, timeout) == osOK) {
LL_CRC_ResetCRCCalculationUnit(CRC);
return true;
}
return false;
}

View File

@@ -6,7 +6,8 @@
#include <stm32wbxx.h>
#define FURI_HAL_TAG "FuriHalFlash"
#define TAG "FuriHalFlash"
#define FURI_HAL_CRITICAL_MSG "Critical flash operation fail"
#define FURI_HAL_FLASH_READ_BLOCK 8
#define FURI_HAL_FLASH_WRITE_BLOCK 8
@@ -14,13 +15,17 @@
#define FURI_HAL_FLASH_CYCLES_COUNT 10000
#define FURI_HAL_FLASH_TIMEOUT 1000
#define FURI_HAL_FLASH_KEY1 0x45670123U
#define FURI_HAL_FLASH_KEY2 0xCDEF89ABU
#define FURI_HAL_FLASH_TOTAL_PAGES 256
#define FURI_HAL_FLASH_SR_ERRORS \
(FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_SIZERR | \
FLASH_SR_PGSERR | FLASH_SR_MISERR | FLASH_SR_FASTERR | FLASH_SR_RDERR | FLASH_SR_OPTVERR)
//#define FURI_HAL_FLASH_OB_START_ADDRESS 0x1FFF8000
#define FURI_HAL_FLASH_OPT_KEY1 0x08192A3B
#define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F
#define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2))
#define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL))
#define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \
(((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \
@@ -88,7 +93,7 @@ static void furi_hal_flash_unlock() {
WRITE_REG(FLASH->KEYR, FURI_HAL_FLASH_KEY1);
WRITE_REG(FLASH->KEYR, FURI_HAL_FLASH_KEY2);
/* verify Flash is unlock */
/* verify Flash is unlocked */
furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) == 0U);
}
@@ -386,4 +391,125 @@ int16_t furi_hal_flash_get_page_number(size_t address) {
}
return (address - flash_base) / FURI_HAL_FLASH_PAGE_SIZE;
}
}
uint32_t furi_hal_flash_ob_get_word(size_t word_idx, bool complementary) {
furi_check(word_idx <= FURI_HAL_FLASH_OB_TOTAL_WORDS);
const uint32_t* ob_data = (const uint32_t*)(OPTION_BYTE_BASE);
size_t raw_word_idx = word_idx * 2;
if(complementary) {
raw_word_idx += 1;
}
return ob_data[raw_word_idx];
}
void furi_hal_flash_ob_unlock() {
furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U);
furi_hal_flash_begin(true);
WRITE_REG(FLASH->OPTKEYR, FURI_HAL_FLASH_OPT_KEY1);
__ISB();
WRITE_REG(FLASH->OPTKEYR, FURI_HAL_FLASH_OPT_KEY2);
/* verify OB area is unlocked */
furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == 0U);
}
void furi_hal_flash_ob_lock() {
furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == 0U);
SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK);
furi_hal_flash_end(true);
furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U);
}
typedef enum {
FuriHalFlashObInvalid,
FuriHalFlashObRegisterUserRead,
FuriHalFlashObRegisterPCROP1AStart,
FuriHalFlashObRegisterPCROP1AEnd,
FuriHalFlashObRegisterWRPA,
FuriHalFlashObRegisterWRPB,
FuriHalFlashObRegisterPCROP1BStart,
FuriHalFlashObRegisterPCROP1BEnd,
FuriHalFlashObRegisterIPCCMail,
FuriHalFlashObRegisterSecureFlash,
FuriHalFlashObRegisterC2Opts,
} FuriHalFlashObRegister;
typedef struct {
FuriHalFlashObRegister ob_reg;
uint32_t* ob_register_address;
} FuriHalFlashObMapping;
#define OB_REG_DEF(INDEX, REG) \
{ .ob_reg = INDEX, .ob_register_address = (uint32_t*)(REG) }
static const FuriHalFlashObMapping furi_hal_flash_ob_reg_map[FURI_HAL_FLASH_OB_TOTAL_WORDS] = {
OB_REG_DEF(FuriHalFlashObRegisterUserRead, (&FLASH->OPTR)),
OB_REG_DEF(FuriHalFlashObRegisterPCROP1AStart, (&FLASH->PCROP1ASR)),
OB_REG_DEF(FuriHalFlashObRegisterPCROP1AEnd, (&FLASH->PCROP1AER)),
OB_REG_DEF(FuriHalFlashObRegisterWRPA, (&FLASH->WRP1AR)),
OB_REG_DEF(FuriHalFlashObRegisterWRPB, (&FLASH->WRP1BR)),
OB_REG_DEF(FuriHalFlashObRegisterPCROP1BStart, (&FLASH->PCROP1BSR)),
OB_REG_DEF(FuriHalFlashObRegisterPCROP1BEnd, (&FLASH->PCROP1BER)),
OB_REG_DEF(FuriHalFlashObInvalid, (NULL)),
OB_REG_DEF(FuriHalFlashObInvalid, (NULL)),
OB_REG_DEF(FuriHalFlashObInvalid, (NULL)),
OB_REG_DEF(FuriHalFlashObInvalid, (NULL)),
OB_REG_DEF(FuriHalFlashObInvalid, (NULL)),
OB_REG_DEF(FuriHalFlashObInvalid, (NULL)),
OB_REG_DEF(FuriHalFlashObRegisterIPCCMail, (NULL)),
OB_REG_DEF(FuriHalFlashObRegisterSecureFlash, (NULL)),
OB_REG_DEF(FuriHalFlashObRegisterC2Opts, (NULL)),
};
void furi_hal_flash_ob_apply() {
furi_hal_flash_ob_unlock();
/* OBL_LAUNCH: When set to 1, this bit forces the option byte reloading.
* It cannot be written if OPTLOCK is set */
SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH);
furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT));
furi_hal_flash_ob_lock();
}
bool furi_hal_flash_ob_set_word(size_t word_idx, const uint32_t value) {
furi_check(word_idx < FURI_HAL_FLASH_OB_TOTAL_WORDS);
const FuriHalFlashObMapping* reg_def = &furi_hal_flash_ob_reg_map[word_idx];
if(reg_def->ob_register_address == NULL) {
FURI_LOG_E(TAG, "Attempt to set RO OB word %d", word_idx);
return false;
}
FURI_LOG_W(
TAG,
"Setting OB reg %d for word %d (addr 0x%08X) to 0x%08X",
reg_def->ob_reg,
word_idx,
reg_def->ob_register_address,
value);
/* 1. Clear OPTLOCK option lock bit with the clearing sequence */
furi_hal_flash_ob_unlock();
/* 2. Write the desired options value in the options registers */
*reg_def->ob_register_address = value;
/* 3. Check that no Flash memory operation is on going by checking the BSY && PESD */
furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT));
while(LL_FLASH_IsActiveFlag_OperationSuspended()) {
osThreadYield();
};
/* 4. Set the Options start bit OPTSTRT */
SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);
/* 5. Wait for the BSY bit to be cleared. */
furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT));
furi_hal_flash_ob_lock();
return true;
}
const FuriHalFlashRawOptionByteData* furi_hal_flash_ob_get_raw_ptr() {
return (const FuriHalFlashRawOptionByteData*)OPTION_BYTE_BASE;
}

View File

@@ -4,6 +4,25 @@
#include <stdint.h>
#include <stddef.h>
#define FURI_HAL_FLASH_OB_RAW_SIZE_BYTES 0x80
#define FURI_HAL_FLASH_OB_SIZE_WORDS (FURI_HAL_FLASH_OB_RAW_SIZE_BYTES / sizeof(uint32_t))
#define FURI_HAL_FLASH_OB_TOTAL_VALUES (FURI_HAL_FLASH_OB_SIZE_WORDS / 2)
typedef union {
uint8_t bytes[FURI_HAL_FLASH_OB_RAW_SIZE_BYTES];
union {
struct {
uint32_t base;
uint32_t complementary_value;
} values;
uint64_t dword;
} obs[FURI_HAL_FLASH_OB_TOTAL_VALUES];
} FuriHalFlashRawOptionByteData;
_Static_assert(
sizeof(FuriHalFlashRawOptionByteData) == FURI_HAL_FLASH_OB_RAW_SIZE_BYTES,
"UpdateManifestOptionByteData size error");
/** Init flash, applying necessary workarounds
*/
void furi_hal_flash_init();
@@ -64,7 +83,7 @@ size_t furi_hal_flash_get_free_page_count();
/** Erase Flash
*
* @warning locking operation with critical section, stales execution
* @warning locking operation with critical section, stalls execution
*
* @param page The page to erase
*
@@ -74,7 +93,7 @@ bool furi_hal_flash_erase(uint8_t page);
/** Write double word (64 bits)
*
* @warning locking operation with critical section, stales execution
* @warning locking operation with critical section, stalls execution
*
* @param address destination address, must be double word aligned.
* @param data data to write
@@ -85,7 +104,7 @@ bool furi_hal_flash_write_dword(size_t address, uint64_t data);
/** Write aligned page data (up to page size)
*
* @warning locking operation with critical section, stales execution
* @warning locking operation with critical section, stalls execution
*
* @param address destination address, must be page aligned.
* @param data data to write
@@ -99,5 +118,27 @@ bool furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16
*
* @return page number, -1 for invalid address
*/
int16_t furi_hal_flash_get_page_number(size_t address);
int16_t furi_hal_flash_get_page_number(size_t address);
/** Writes OB word, using non-compl. index of register in Flash, OPTION_BYTE_BASE
*
* @warning locking operation with critical section, stalls execution
*
* @param word_idx OB word number
* @param value data to write
* @return true if value was written, false for read-only word
*/
bool furi_hal_flash_ob_set_word(size_t word_idx, const uint32_t value);
/** Forces a reload of OB data from flash to registers
*
* @warning Initializes system restart
*
*/
void furi_hal_flash_ob_apply();
/** Get raw OB storage data
*
* @return pointer to read-only data of OB (raw + complementary values)
*/
const FuriHalFlashRawOptionByteData* furi_hal_flash_ob_get_raw_ptr();

View File

@@ -66,44 +66,45 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) {
out("firmware_target", string_get_cstr(value), false, context);
}
WirelessFwInfo_t pWirelessInfo;
if(furi_hal_bt_is_alive() && SHCI_GetWirelessFwInfo(&pWirelessInfo) == SHCI_Success) {
if(furi_hal_bt_is_alive()) {
const BleGlueC2Info* ble_c2_info = ble_glue_get_c2_info();
out("radio_alive", "true", false, context);
out("radio_mode", ble_c2_info->mode == BleGlueC2ModeFUS ? "FUS" : "Stack", false, context);
// FUS Info
string_printf(value, "%d", pWirelessInfo.FusVersionMajor);
string_printf(value, "%d", ble_c2_info->FusVersionMajor);
out("radio_fus_major", string_get_cstr(value), false, context);
string_printf(value, "%d", pWirelessInfo.FusVersionMinor);
string_printf(value, "%d", ble_c2_info->FusVersionMinor);
out("radio_fus_minor", string_get_cstr(value), false, context);
string_printf(value, "%d", pWirelessInfo.FusVersionSub);
string_printf(value, "%d", ble_c2_info->FusVersionSub);
out("radio_fus_sub", string_get_cstr(value), false, context);
string_printf(value, "%dK", pWirelessInfo.FusMemorySizeSram2B);
string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2B);
out("radio_fus_sram2b", string_get_cstr(value), false, context);
string_printf(value, "%dK", pWirelessInfo.FusMemorySizeSram2A);
string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2A);
out("radio_fus_sram2a", string_get_cstr(value), false, context);
string_printf(value, "%dK", pWirelessInfo.FusMemorySizeFlash * 4);
string_printf(value, "%dK", ble_c2_info->FusMemorySizeFlash * 4);
out("radio_fus_flash", string_get_cstr(value), false, context);
// Stack Info
string_printf(value, "%d", pWirelessInfo.StackType);
string_printf(value, "%d", ble_c2_info->StackType);
out("radio_stack_type", string_get_cstr(value), false, context);
string_printf(value, "%d", pWirelessInfo.VersionMajor);
string_printf(value, "%d", ble_c2_info->VersionMajor);
out("radio_stack_major", string_get_cstr(value), false, context);
string_printf(value, "%d", pWirelessInfo.VersionMinor);
string_printf(value, "%d", ble_c2_info->VersionMinor);
out("radio_stack_minor", string_get_cstr(value), false, context);
string_printf(value, "%d", pWirelessInfo.VersionSub);
string_printf(value, "%d", ble_c2_info->VersionSub);
out("radio_stack_sub", string_get_cstr(value), false, context);
string_printf(value, "%d", pWirelessInfo.VersionBranch);
string_printf(value, "%d", ble_c2_info->VersionBranch);
out("radio_stack_branch", string_get_cstr(value), false, context);
string_printf(value, "%d", pWirelessInfo.VersionReleaseType);
string_printf(value, "%d", ble_c2_info->VersionReleaseType);
out("radio_stack_release", string_get_cstr(value), false, context);
string_printf(value, "%dK", pWirelessInfo.MemorySizeSram2B);
string_printf(value, "%dK", ble_c2_info->MemorySizeSram2B);
out("radio_stack_sram2b", string_get_cstr(value), false, context);
string_printf(value, "%dK", pWirelessInfo.MemorySizeSram2A);
string_printf(value, "%dK", ble_c2_info->MemorySizeSram2A);
out("radio_stack_sram2a", string_get_cstr(value), false, context);
string_printf(value, "%dK", pWirelessInfo.MemorySizeSram1);
string_printf(value, "%dK", ble_c2_info->MemorySizeSram1);
out("radio_stack_sram1", string_get_cstr(value), false, context);
string_printf(value, "%dK", pWirelessInfo.MemorySizeFlash * 4);
string_printf(value, "%dK", ble_c2_info->MemorySizeFlash * 4);
out("radio_stack_flash", string_get_cstr(value), false, context);
// Mac address

View File

@@ -64,7 +64,7 @@ void furi_hal_vcp_init() {
vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1);
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
FURI_LOG_W(TAG, "Skipped worker init: device in special startup mode=");
FURI_LOG_W(TAG, "Skipped worker init: device in special startup mode");
return;
}