[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

@@ -7,7 +7,8 @@
#include <flipper_format/flipper_format.h>
#include <update_util/update_manifest.h>
#include <lib/toolbox/path.h>
#include <toolbox/path.h>
#include <toolbox/crc32_calc.h>
static FATFS* pfs = NULL;
@@ -27,7 +28,6 @@ static bool flipper_update_init() {
furi_hal_delay_init();
furi_hal_spi_init();
furi_hal_crc_init(false);
MX_FATFS_Init();
if(!hal_sd_detect()) {
@@ -62,17 +62,15 @@ static bool flipper_update_load_stage(const string_t work_dir, UpdateManifest* m
uint32_t bytes_read = 0;
const uint16_t MAX_READ = 0xFFFF;
furi_hal_crc_reset();
uint32_t crc = 0;
do {
uint16_t size_read = 0;
if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) {
break;
}
crc = furi_hal_crc_feed(img + bytes_read, size_read);
crc = crc32_calc_buffer(crc, img + bytes_read, size_read);
bytes_read += size_read;
} while(bytes_read == MAX_READ);
furi_hal_crc_reset();
do {
if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) {

View File

@@ -6,7 +6,9 @@
#include "shci.h"
#include "shci_tl.h"
#include "app_debug.h"
#include <furi_hal.h>
#include <shci/shci.h>
#define TAG "Core2"
@@ -27,22 +29,13 @@ PLACE_IN_SECTION("MB_MEM2")
ALIGN(4)
static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
typedef enum {
// Stage 1: core2 startup and FUS
BleGlueStatusStartup,
BleGlueStatusBroken,
BleGlueStatusFusStarted,
// Stage 2: radio stack
BleGlueStatusRadioStackStarted,
BleGlueStatusRadioStackMissing
} BleGlueStatus;
typedef struct {
osMutexId_t shci_mtx;
osSemaphoreId_t shci_sem;
FuriThread* thread;
BleGlueStatus status;
BleGlueKeyStorageChangedCallback callback;
BleGlueC2Info c2_info;
void* context;
} BleGlue;
@@ -111,33 +104,96 @@ void ble_glue_init() {
*/
}
bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info) {
bool ret = false;
const BleGlueC2Info* ble_glue_get_c2_info() {
return &ble_glue->c2_info;
}
size_t countdown = 1000;
while(countdown > 0) {
if(ble_glue->status == BleGlueStatusFusStarted) {
ret = true;
break;
BleGlueStatus ble_glue_get_c2_status() {
return ble_glue->status;
}
static void ble_glue_update_c2_fw_info() {
WirelessFwInfo_t wireless_info;
SHCI_GetWirelessFwInfo(&wireless_info);
BleGlueC2Info* local_info = &ble_glue->c2_info;
local_info->VersionMajor = wireless_info.VersionMajor;
local_info->VersionMinor = wireless_info.VersionMinor;
local_info->VersionMajor = wireless_info.VersionMajor;
local_info->VersionMinor = wireless_info.VersionMinor;
local_info->VersionSub = wireless_info.VersionSub;
local_info->VersionBranch = wireless_info.VersionBranch;
local_info->VersionReleaseType = wireless_info.VersionReleaseType;
local_info->MemorySizeSram2B = wireless_info.MemorySizeSram2B;
local_info->MemorySizeSram2A = wireless_info.MemorySizeSram2A;
local_info->MemorySizeSram1 = wireless_info.MemorySizeSram1;
local_info->MemorySizeFlash = wireless_info.MemorySizeFlash;
local_info->StackType = wireless_info.StackType;
local_info->FusVersionMajor = wireless_info.FusVersionMajor;
local_info->FusVersionMinor = wireless_info.FusVersionMinor;
local_info->FusVersionSub = wireless_info.FusVersionSub;
local_info->FusMemorySizeSram2B = wireless_info.FusMemorySizeSram2B;
local_info->FusMemorySizeSram2A = wireless_info.FusMemorySizeSram2A;
local_info->FusMemorySizeFlash = wireless_info.FusMemorySizeFlash;
}
static void ble_glue_dump_stack_info() {
const BleGlueC2Info* c2_info = &ble_glue->c2_info;
FURI_LOG_I(
TAG,
"Core2: FUS: %d.%d.%d, mem %d/%d, flash %d pages",
c2_info->FusVersionMajor,
c2_info->FusVersionMinor,
c2_info->FusVersionSub,
c2_info->FusMemorySizeSram2B,
c2_info->FusMemorySizeSram2A,
c2_info->FusMemorySizeFlash);
FURI_LOG_I(
TAG,
"Core2: Stack: %d.%d.%d, branch %d, reltype %d, stacktype %d, flash %d pages",
c2_info->VersionMajor,
c2_info->VersionMinor,
c2_info->VersionSub,
c2_info->VersionBranch,
c2_info->VersionReleaseType,
c2_info->StackType,
c2_info->MemorySizeFlash);
}
bool ble_glue_wait_for_c2_start(int32_t timeout) {
bool started = false;
do {
// TODO: use mutex?
started = ble_glue->status == BleGlueStatusC2Started;
if(!started) {
timeout--;
osDelay(1);
}
countdown--;
osDelay(1);
}
} while(!started && (timeout > 0));
if(ble_glue->status == BleGlueStatusFusStarted) {
SHCI_GetWirelessFwInfo(info);
if(started) {
FURI_LOG_I(
TAG,
"C2 boot completed, mode: %s",
ble_glue->c2_info.mode == BleGlueC2ModeFUS ? "FUS" : "Stack");
ble_glue_update_c2_fw_info();
ble_glue_dump_stack_info();
} else {
FURI_LOG_E(TAG, "Failed to start FUS");
FURI_LOG_E(TAG, "C2 startup failed");
ble_glue->status = BleGlueStatusBroken;
}
return ret;
return started;
}
bool ble_glue_start() {
furi_assert(ble_glue);
if(ble_glue->status != BleGlueStatusFusStarted) {
if(ble_glue->status != BleGlueStatusC2Started) {
return false;
}
@@ -145,7 +201,7 @@ bool ble_glue_start() {
furi_hal_power_insomnia_enter();
if(ble_app_init()) {
FURI_LOG_I(TAG, "Radio stack started");
ble_glue->status = BleGlueStatusRadioStackStarted;
ble_glue->status = BleGlueStatusRadioStackRunning;
ret = true;
if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) {
FURI_LOG_I(TAG, "Flash activity control switched to SEM7");
@@ -167,7 +223,7 @@ bool ble_glue_is_alive() {
return false;
}
return ble_glue->status >= BleGlueStatusFusStarted;
return ble_glue->status >= BleGlueStatusC2Started;
}
bool ble_glue_is_radio_stack_ready() {
@@ -175,26 +231,42 @@ bool ble_glue_is_radio_stack_ready() {
return false;
}
return ble_glue->status == BleGlueStatusRadioStackStarted;
return ble_glue->status == BleGlueStatusRadioStackRunning;
}
bool ble_glue_radio_stack_fw_launch_started() {
bool ret = false;
// Get FUS status
SHCI_FUS_GetState_ErrorCode_t err_code = 0;
uint8_t state = SHCI_C2_FUS_GetState(&err_code);
if(state == FUS_STATE_VALUE_IDLE) {
// When FUS is running we can't read radio stack version correctly
// Trying to start radio stack fw, which leads to reset
FURI_LOG_W(TAG, "FUS is running. Restart to launch Radio Stack");
BleGlueCommandResult ble_glue_force_c2_mode(BleGlueC2Mode desired_mode) {
furi_check(desired_mode > BleGlueC2ModeUnknown);
if(desired_mode == ble_glue->c2_info.mode) {
return BleGlueCommandResultOK;
}
if((ble_glue->c2_info.mode == BleGlueC2ModeFUS) && (desired_mode == BleGlueC2ModeStack)) {
if((ble_glue->c2_info.VersionMajor == 0) && (ble_glue->c2_info.VersionMinor == 0)) {
FURI_LOG_W(TAG, "Stack isn't installed!");
return BleGlueCommandResultError;
}
SHCI_CmdStatus_t status = SHCI_C2_FUS_StartWs();
if(status) {
FURI_LOG_E(TAG, "Failed to start Radio Stack with status: %02X", status);
} else {
ret = true;
return BleGlueCommandResultError;
}
return BleGlueCommandResultRestartPending;
}
return ret;
if((ble_glue->c2_info.mode == BleGlueC2ModeStack) && (desired_mode == BleGlueC2ModeFUS)) {
SHCI_FUS_GetState_ErrorCode_t error_code = 0;
uint8_t fus_state = SHCI_C2_FUS_GetState(&error_code);
FURI_LOG_D(TAG, "FUS state: %X, error = %x", fus_state, error_code);
if(fus_state == SHCI_FUS_CMD_NOT_SUPPORTED) {
// Second call to SHCI_C2_FUS_GetState() restarts whole MCU & boots FUS
fus_state = SHCI_C2_FUS_GetState(&error_code);
FURI_LOG_D(TAG, "FUS state#2: %X, error = %x", fus_state, error_code);
return BleGlueCommandResultRestartPending;
}
return BleGlueCommandResultOK;
}
return BleGlueCommandResultError;
}
static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) {
@@ -228,8 +300,15 @@ static void ble_glue_sys_user_event_callback(void* pPayload) {
(TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload);
if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) {
FURI_LOG_I(TAG, "Fus started");
ble_glue->status = BleGlueStatusFusStarted;
FURI_LOG_I(TAG, "Core2 started");
SHCI_C2_Ready_Evt_t* p_c2_ready_evt = (SHCI_C2_Ready_Evt_t*)p_sys_event->payload;
if(p_c2_ready_evt->sysevt_ready_rsp == WIRELESS_FW_RUNNING) {
ble_glue->c2_info.mode = BleGlueC2ModeStack;
} else if(p_c2_ready_evt->sysevt_ready_rsp == FUS_FW_RUNNING) {
ble_glue->c2_info.mode = BleGlueC2ModeFUS;
}
ble_glue->status = BleGlueStatusC2Started;
furi_hal_power_insomnia_exit();
} else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) {
FURI_LOG_E(TAG, "Error during initialization");
@@ -308,3 +387,61 @@ void shci_cmd_resp_wait(uint32_t timeout) {
osSemaphoreAcquire(ble_glue->shci_sem, osWaitForever);
}
}
bool ble_glue_reinit_c2() {
return SHCI_C2_Reinit() == SHCI_Success;
}
BleGlueCommandResult ble_glue_fus_stack_delete() {
FURI_LOG_I(TAG, "Erasing stack");
SHCI_CmdStatus_t erase_stat = SHCI_C2_FUS_FwDelete();
FURI_LOG_I(TAG, "Cmd res = %x", erase_stat);
if(erase_stat == SHCI_Success) {
return BleGlueCommandResultOperationOngoing;
}
ble_glue_fus_get_status();
return BleGlueCommandResultError;
}
BleGlueCommandResult ble_glue_fus_stack_install(uint32_t src_addr, uint32_t dst_addr) {
FURI_LOG_I(TAG, "Installing stack");
SHCI_CmdStatus_t write_stat = SHCI_C2_FUS_FwUpgrade(src_addr, dst_addr);
FURI_LOG_I(TAG, "Cmd res = %x", write_stat);
if(write_stat == SHCI_Success) {
return BleGlueCommandResultOperationOngoing;
}
ble_glue_fus_get_status();
return BleGlueCommandResultError;
}
BleGlueCommandResult ble_glue_fus_get_status() {
furi_check(ble_glue->c2_info.mode == BleGlueC2ModeFUS);
SHCI_FUS_GetState_ErrorCode_t error_code = 0;
uint8_t fus_state = SHCI_C2_FUS_GetState(&error_code);
FURI_LOG_I(TAG, "FUS state: %x, error: %x", fus_state, error_code);
if((error_code != 0) || (fus_state == FUS_STATE_VALUE_ERROR)) {
return BleGlueCommandResultError;
} else if(
(fus_state >= FUS_STATE_VALUE_FW_UPGRD_ONGOING) &&
(fus_state <= FUS_STATE_VALUE_SERVICE_ONGOING_END)) {
return BleGlueCommandResultOperationOngoing;
}
return BleGlueCommandResultOK;
}
BleGlueCommandResult ble_glue_fus_wait_operation() {
furi_check(ble_glue->c2_info.mode == BleGlueC2ModeFUS);
bool wip;
do {
BleGlueCommandResult fus_status = ble_glue_fus_get_status();
if(fus_status == BleGlueCommandResultError) {
return BleGlueCommandResultError;
}
wip = fus_status == BleGlueCommandResultOperationOngoing;
if(wip) {
osDelay(20);
}
} while(wip);
return BleGlueCommandResultOK;
}

View File

@@ -2,12 +2,53 @@
#include <stdint.h>
#include <stdbool.h>
#include <shci/shci.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
BleGlueC2ModeUnknown = 0,
BleGlueC2ModeFUS,
BleGlueC2ModeStack,
} BleGlueC2Mode;
typedef struct {
BleGlueC2Mode mode;
/**
* Wireless Info
*/
uint8_t VersionMajor;
uint8_t VersionMinor;
uint8_t VersionSub;
uint8_t VersionBranch;
uint8_t VersionReleaseType;
uint8_t MemorySizeSram2B; /*< Multiple of 1K */
uint8_t MemorySizeSram2A; /*< Multiple of 1K */
uint8_t MemorySizeSram1; /*< Multiple of 1K */
uint8_t MemorySizeFlash; /*< Multiple of 4K */
uint8_t StackType;
/**
* Fus Info
*/
uint8_t FusVersionMajor;
uint8_t FusVersionMinor;
uint8_t FusVersionSub;
uint8_t FusMemorySizeSram2B; /*< Multiple of 1K */
uint8_t FusMemorySizeSram2A; /*< Multiple of 1K */
uint8_t FusMemorySizeFlash; /*< Multiple of 4K */
} BleGlueC2Info;
typedef enum {
// Stage 1: core2 startup and FUS
BleGlueStatusStartup,
BleGlueStatusBroken,
BleGlueStatusC2Started,
// Stage 2: radio stack
BleGlueStatusRadioStackRunning,
BleGlueStatusRadioStackMissing
} BleGlueStatus;
typedef void (
*BleGlueKeyStorageChangedCallback)(uint8_t* change_addr_start, uint16_t size, void* context);
@@ -26,7 +67,15 @@ bool ble_glue_start();
*/
bool ble_glue_is_alive();
bool ble_glue_wait_for_fus_start(WirelessFwInfo_t* info);
/** Waits for C2 to reports its mode to callback
*
* @return true if it reported before reaching timeout
*/
bool ble_glue_wait_for_c2_start(int32_t timeout);
BleGlueStatus ble_glue_get_c2_status();
const BleGlueC2Info* ble_glue_get_c2_info();
/** Is core2 radio stack present and ready
*
@@ -46,12 +95,30 @@ void ble_glue_set_key_storage_changed_callback(
/** Stop SHCI thread */
void ble_glue_thread_stop();
bool ble_glue_reinit_c2();
typedef enum {
BleGlueCommandResultUnknown,
BleGlueCommandResultOK,
BleGlueCommandResultError,
BleGlueCommandResultRestartPending,
BleGlueCommandResultOperationOngoing,
} BleGlueCommandResult;
/** Restart MCU to launch radio stack firmware if necessary
*
* @return true on radio stack start command
*/
bool ble_glue_radio_stack_fw_launch_started();
BleGlueCommandResult ble_glue_force_c2_mode(BleGlueC2Mode mode);
BleGlueCommandResult ble_glue_fus_stack_delete();
BleGlueCommandResult ble_glue_fus_stack_install(uint32_t src_addr, uint32_t dst_addr);
BleGlueCommandResult ble_glue_fus_get_status();
BleGlueCommandResult ble_glue_fus_wait_operation();
#ifdef __cplusplus
}
#endif
#endif

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;
}

View File

@@ -39,7 +39,6 @@ template <unsigned int N> struct STOP_EXTERNING_ME {};
#include "furi_hal_uart.h"
#include "furi_hal_info.h"
#include "furi_hal_random.h"
#include "furi_hal_crc.h"
#ifdef __cplusplus
extern "C" {

View File

@@ -16,6 +16,7 @@
#define FURI_HAL_BT_STACK_VERSION_MAJOR (1)
#define FURI_HAL_BT_STACK_VERSION_MINOR (13)
#define FURI_HAL_BT_C2_START_TIMEOUT 1000
#ifdef __cplusplus
extern "C" {
@@ -207,6 +208,12 @@ bool furi_hal_bt_start_scan(GapScanCallback callback, void* context);
/** Stop MAC addresses scan */
void furi_hal_bt_stop_scan();
/** Check & switch C2 to given mode
*
* @param[in] mode mode to switch into
*/
bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode);
#ifdef __cplusplus
}
#endif

View File

@@ -28,6 +28,7 @@ typedef enum {
FuriHalRtcFlagDebug = (1 << 0),
FuriHalRtcFlagFactoryReset = (1 << 1),
FuriHalRtcFlagLock = (1 << 2),
FuriHalRtcFlagC2Update = (1 << 3),
} FuriHalRtcFlag;
typedef enum {