STM32安全固件升级:使用自定义 bootloader 实现SD卡固件升级,包含固件加密

发布于:2025-07-02 ⋅ 阅读:(20) ⋅ 点赞:(0)

前言

在 STM32 嵌入式开发中,Bootloader 是一个不可或缺的模块。ST 公司为 STM32 提供了功能完备的官方 Bootloader,支持多种通信接口(如 USART、USB DFU、I2C、SPI 等),适用于标准的固件更新方案。

然而,随着系统需求的多样化和定制化,自定义 Bootloader 越来越成为项目开发者的首选。

方案比较

官方 bootloader 优点

  1. 开箱即用:无需编写任何代码,即可通过 ROM 中的 Bootloader 进行下载。
  2. 支持多种接口:如 USART、USB、I2C 等,适合简单的量产烧录。
  3. 稳定性高:由 ST 官方提供,经过长期验证。

官方 bootloader 局限

  1. 不可修改
    官方 Bootloader 存储在芯片的系统 ROM 中,属于只读区域,无法添加用户逻辑或扩展功能。
  2. 接口受限
    实际项目中可能需要使用 CAN、以太网、BLE 等接口更新固件,而这些通常未被官方 Bootloader 支持或实现较复杂。
  3. 协议封闭
    官方 Bootloader 通信协议较为复杂,难以与高层系统(如云 OTA、手机 App)灵活集成。
  4. 安全性不足
    默认无固件加密或签名机制,容易被反编译和恶意刷写。
  5. 启动方式有限
    官方 Bootloader 通常通过 BOOT 引脚或 Option Byte 来触发,不够灵活。例如,无法通过软件条件(如特定按键组合)进入 Bootloader 模式。

通过自定义bootloader,可以实现多种方式控制触发升级逻辑:

  • 按键时序组合
  • 接收特定指令
  • Flash 中标志位
  • RTC Backup 寄存器
  • Watchdog Timeout
  • 远程 OTA

主要思想

本项目实现了一个从 TF 读取升级固件并更新到 flash 中的功能,同时包括了绑定 chipID 加密操作,提升操作便利性的同时提供了绝对的安全可靠性。

主要思想史利用单片机的唯一的ChipID于用户自定义的密钥输入sha256算法得出32字节的加密密钥,然后用这个密钥对固件进行异或运算加密

输入
输入
ChipID
SHA-256
用户密钥
32字节加密密钥
异或加密固件

设计细节

固件加密

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <shlobj.h>
#include <commctrl.h>

extern "C" {
#include "../Core/Inc/sha256.h"
}

#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "shell32.lib")

// 控件ID定义
#define IDC_BROWSE_CID      101
#define IDC_BROWSE_APP      102
#define IDC_BROWSE_OUTPUT   103
#define IDC_START           104
#define IDC_EDIT_CID        105
#define IDC_EDIT_APP        106
#define IDC_EDIT_OUTPUT     107
#define IDC_LOG             108

#define HASH_SIZE 32

// 全局变量
uint32_t chipID[3];
uint8_t encryptionKey[HASH_SIZE];
HWND g_hEditLog = NULL;
const char* userKey = "Your_Password";

// 函数声明
void generate_encryption_key(const uint32_t* chipID, const char* userKey, uint8_t* encryptionKey);
void xor_encrypt(FILE* src, FILE* dst, const uint8_t* key);
void calculate_file_hash(FILE* file, uint8_t* hash);
int parse_chip_id(const wchar_t* filename, uint32_t* chipID);
void create_firmware_file(FILE* src, const uint32_t* chipID, const uint8_t* key, const wchar_t* outputPath);
int process_all_cid_files(const wchar_t* cidDir, const wchar_t* appPath, const wchar_t* outputDir);
wchar_t* browse_folder(const wchar_t* title);
wchar_t* browse_file(const wchar_t* title, const wchar_t* filter);
void LogMessage(const wchar_t* format, ...);

// 生成加密密钥
void generate_encryption_key(const uint32_t* chipID, const char* userKey, uint8_t* encryptionKey) {
    SHA256_CTX ctx;
    sha256_init(&ctx);
    sha256_update(&ctx, (const uint8_t*)chipID, sizeof(uint32_t) * 3);
    sha256_update(&ctx, (const uint8_t*)userKey, strlen(userKey));
    sha256_final(&ctx, encryptionKey);
}

// 异或加密
void xor_encrypt(FILE* src, FILE* dst, const uint8_t* key) {
    uint8_t buffer[4096];
    size_t bytes_read;
    size_t key_index = 0;
    fseek(src, 0, SEEK_SET);

    while ((bytes_read = fread(buffer, 1, sizeof(buffer), src)) > 0) {
        for (size_t i = 0; i < bytes_read; i++) {
            buffer[i] ^= key[key_index];
            key_index = (key_index + 1) % HASH_SIZE;
        }
        fwrite(buffer, 1, bytes_read, dst);
    }
}

// 计算文件哈希
void calculate_file_hash(FILE* file, uint8_t* hash) {
    SHA256_CTX ctx;
    sha256_init(&ctx);
    uint8_t buffer[4096];
    size_t bytes_read;
    fseek(file, 0, SEEK_SET);

    while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
        sha256_update(&ctx, buffer, bytes_read);
    }
    sha256_final(&ctx, hash);
}

// 解析芯片ID
int parse_chip_id(const wchar_t* filename, uint32_t* chipID) {
    FILE* cidFile = _wfopen(filename, L"r");
    if (!cidFile) {
        LogMessage(L"错误: 无法打开芯片ID文件: %s\n", filename);
        return 0;
    }

    char line[50];
    if (!fgets(line, sizeof(line), cidFile)) {
        LogMessage(L"读取文件错误\n");
        fclose(cidFile);
        return 0;
    }
    fclose(cidFile);

    char* token = strtok(line, "-");
    for (int i = 0; i < 3 && token != NULL; i++) {
        char* hex_str = (token[0] == '0' && token[1] == 'x') ? token + 2 : token;
        chipID[i] = strtoul(hex_str, NULL, 16);
        token = strtok(NULL, "-");
    }
    return 1;
}

// 创建固件文件
void create_firmware_file(FILE* src, const uint32_t* chipID, const uint8_t* key, const wchar_t* outputPath) {
    wchar_t firmware_filename[MAX_PATH];
    swprintf(firmware_filename, MAX_PATH,
        L"%s\\0x%08X-0x%08X-0x%08X.bin",
        outputPath, chipID[0], chipID[1], chipID[2]);

    FILE* dst = _wfopen(firmware_filename, L"wb");
    if (!dst) {
        LogMessage(L"错误: 无法创建固件文件: %s\n", firmware_filename);
        return;
    }

    // 计算并写入文件哈希
    uint8_t fileHash[HASH_SIZE];
    calculate_file_hash(src, fileHash);
    fseek(dst, 0, SEEK_SET);
    if (fwrite(fileHash, 1, HASH_SIZE, dst) != HASH_SIZE) {
        LogMessage(L"写入哈希头失败\n");
        fclose(dst);
        return;
    }

    // 执行加密并保存
    xor_encrypt(src, dst, key);
    fclose(dst);
    LogMessage(L"已创建: %s\n", firmware_filename);
}

// 处理所有CID文件
int process_all_cid_files(const wchar_t* cidDir, const wchar_t* appPath, const wchar_t* outputDir) {
    WIN32_FIND_DATAW findData;
    wchar_t searchPath[MAX_PATH];
    swprintf(searchPath, MAX_PATH, L"%s\\*.cid", cidDir);

    HANDLE hFind = FindFirstFileW(searchPath, &findData);
    if (hFind == INVALID_HANDLE_VALUE) {
        LogMessage(L"在目录中未找到CID文件: %s\n", cidDir);
        return 0;
    }

    FILE* appSrc = _wfopen(appPath, L"rb");
    if (!appSrc) {
        LogMessage(L"错误: 无法打开app.bin文件: %s\n", appPath);
        FindClose(hFind);
        return 0;
    }

    int processed = 0;
    do {
        if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            continue;

        wchar_t cidPath[MAX_PATH];
        swprintf(cidPath, MAX_PATH, L"%s\\%s", cidDir, findData.cFileName);
        LogMessage(L"\n处理: %s\n", findData.cFileName);

        if (!parse_chip_id(cidPath, chipID)) {
            LogMessage(L"跳过: %s (解析错误)\n", findData.cFileName);
            continue;
        }

        generate_encryption_key(chipID, userKey, encryptionKey);
        create_firmware_file(appSrc, chipID, encryptionKey, outputDir);
        processed++;

        fseek(appSrc, 0, SEEK_SET);
    } while (FindNextFileW(hFind, &findData));

    fclose(appSrc);
    FindClose(hFind);
    return processed;
}

// 文件夹浏览对话框
wchar_t* browse_folder(const wchar_t* title) {
    BROWSEINFOW bi = { 0 };
    bi.lpszTitle = title;
    bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;

    LPITEMIDLIST pidl = SHBrowseForFolderW(&bi);
    if (pidl) {
        static wchar_t path[MAX_PATH];
        SHGetPathFromIDListW(pidl, path);
        CoTaskMemFree(pidl);
        return path;
    }
    return NULL;
}

// 文件浏览对话框
wchar_t* browse_file(const wchar_t* title, const wchar_t* filter) {
    OPENFILENAMEW ofn = { 0 };
    static wchar_t path[MAX_PATH] = L"";

    ofn.lStructSize = sizeof(OPENFILENAMEW);
    ofn.lpstrTitle = title;
    ofn.lpstrFilter = filter;
    ofn.nFilterIndex = 1;
    ofn.lpstrFile = path;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;

    if (GetOpenFileNameW(&ofn)) {
        return path;
    }
    return NULL;
}

// 日志输出函数
void LogMessage(const wchar_t* format, ...) {
    va_list args;
    va_start(args, format);

    wchar_t buffer[1024];
    vswprintf(buffer, ARRAYSIZE(buffer), format, args);

    // 获取当前文本长度
    int len = GetWindowTextLength(g_hEditLog);
    SendMessage(g_hEditLog, EM_SETSEL, len, len);
    SendMessage(g_hEditLog, EM_REPLACESEL, FALSE, (LPARAM)buffer);

    va_end(args);
}

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_CREATE: {
        // 创建CID目录控件
        CreateWindowW(L"STATIC", L"CID文件目录:", WS_VISIBLE | WS_CHILD,
            20, 20, 100, 25, hWnd, NULL, NULL, NULL);
        CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
            130, 20, 300, 25, hWnd, (HMENU)IDC_EDIT_CID, NULL, NULL);
        CreateWindowW(L"BUTTON", L"浏览...", WS_VISIBLE | WS_CHILD,
            440, 20, 80, 25, hWnd, (HMENU)IDC_BROWSE_CID, NULL, NULL);

        // 创建APP文件控件
        CreateWindowW(L"STATIC", L"APP文件:", WS_VISIBLE | WS_CHILD,
            20, 55, 100, 25, hWnd, NULL, NULL, NULL);
        CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
            130, 55, 300, 25, hWnd, (HMENU)IDC_EDIT_APP, NULL, NULL);
        CreateWindowW(L"BUTTON", L"浏览...", WS_VISIBLE | WS_CHILD,
            440, 55, 80, 25, hWnd, (HMENU)IDC_BROWSE_APP, NULL, NULL);

        // 创建输出目录控件
        CreateWindowW(L"STATIC", L"输出目录:", WS_VISIBLE | WS_CHILD,
            20, 90, 100, 25, hWnd, NULL, NULL, NULL);
        CreateWindowW(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
            130, 90, 300, 25, hWnd, (HMENU)IDC_EDIT_OUTPUT, NULL, NULL);
        CreateWindowW(L"BUTTON", L"浏览...", WS_VISIBLE | WS_CHILD,
            440, 90, 80, 25, hWnd, (HMENU)IDC_BROWSE_OUTPUT, NULL, NULL);

        // 创建操作按钮
        CreateWindowW(L"BUTTON", L"开始处理", WS_VISIBLE | WS_CHILD,
            200, 130, 100, 30, hWnd, (HMENU)IDC_START, NULL, NULL);

        // 创建日志框
        g_hEditLog = CreateWindowW(L"EDIT", L"",
            WS_VISIBLE | WS_CHILD | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL,
            20, 170, 500, 200, hWnd, (HMENU)IDC_LOG, NULL, NULL);

        // 设置日志框字体
        HFONT hFont = CreateFont(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
            DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
            DEFAULT_QUALITY, DEFAULT_PITCH, L"宋体");
        SendMessage(g_hEditLog, WM_SETFONT, (WPARAM)hFont, TRUE);

        break;
    }

    case WM_COMMAND: {
        switch (LOWORD(wParam)) {
        case IDC_BROWSE_CID: {
            wchar_t* path = browse_folder(L"选择包含CID文件的文件夹");
            if (path) {
                SetDlgItemTextW(hWnd, IDC_EDIT_CID, path);
                LogMessage(L"已选择CID目录: %s\n", path);
            }
            break;
        }

        case IDC_BROWSE_APP: {
            wchar_t* path = browse_file(L"选择app.bin文件", L"Bin文件\0*.bin\0所有文件\0*.*\0");
            if (path) {
                SetDlgItemTextW(hWnd, IDC_EDIT_APP, path);
                LogMessage(L"已选择APP文件: %s\n", path);
            }
            break;
        }

        case IDC_BROWSE_OUTPUT: {
            wchar_t* path = browse_folder(L"选择输出文件夹");
            if (path) {
                SetDlgItemTextW(hWnd, IDC_EDIT_OUTPUT, path);
                LogMessage(L"已选择输出目录: %s\n", path);
            }
            break;
        }

        case IDC_START: {
            wchar_t cidDir[MAX_PATH] = { 0 };
            wchar_t appPath[MAX_PATH] = { 0 };
            wchar_t outputDir[MAX_PATH] = { 0 };

            GetDlgItemTextW(hWnd, IDC_EDIT_CID, cidDir, MAX_PATH);
            GetDlgItemTextW(hWnd, IDC_EDIT_APP, appPath, MAX_PATH);
            GetDlgItemTextW(hWnd, IDC_EDIT_OUTPUT, outputDir, MAX_PATH);

            if (!cidDir[0] || !appPath[0] || !outputDir[0]) {
                LogMessage(L"错误: 请先选择所有路径!\n");
                break;
            }

            LogMessage(L"\n=== 批量处理开始 ===\n");
            LogMessage(L"CID目录: %s\n", cidDir);
            LogMessage(L"App文件: %s\n", appPath);
            LogMessage(L"输出目录: %s\n\n", outputDir);

            int count = process_all_cid_files(cidDir, appPath, outputDir);
            LogMessage(L"\n完成: 已处理 %d 个文件\n", count);
            break;
        }
        }
        break;
    }

    case WM_CLOSE:
        DestroyWindow(hWnd);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }
    return 0;
}

// 程序入口
int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    HWND console = GetConsoleWindow();
    if (console) {
        ShowWindow(console, SW_HIDE); // 隐藏现有控制台
    }

    // 初始化通用控件
    INITCOMMONCONTROLSEX icc = { sizeof(INITCOMMONCONTROLSEX), ICC_STANDARD_CLASSES };
    InitCommonControlsEx(&icc);

    // 注册窗口类
    WNDCLASSEXW wc = { sizeof(WNDCLASSEX) };
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = L"FirmwareEncryptor";

    if (!RegisterClassExW(&wc)) {
        return 0;
    }

    // 创建主窗口
    HWND hWnd = CreateWindowW(wc.lpszClassName, L"Bootloader固件加密工具",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT, 550, 450,
        NULL, NULL, hInstance, NULL);

    if (!hWnd) {
        return 0;
    }

    // 显示窗口
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    // 新增居中代码
    RECT rect;
    GetWindowRect(hWnd, &rect); // 获取窗口尺寸
    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = GetSystemMetrics(SM_CYSCREEN);
    int x = (screenWidth - (rect.right - rect.left)) / 2;
    int y = (screenHeight - (rect.bottom - rect.top)) / 2;
    SetWindowPos(hWnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); // 移动到中心

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

固件解密

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "fatfs.h"
#include "sdio.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "stm32f4xx_hal.h"
#include "stdio.h"
#include "sha256.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

#define MODE_PORT          GPIOD
#define MODE_PIN           GPIO_PIN_13
#define LED_PORT           GPIOD
#define LED_PIN            GPIO_PIN_14

#define READ_MODE_PIN()    HAL_GPIO_ReadPin(MODE_PORT, MODE_PIN)
#define SET_LED_ON()       HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET)
#define SET_LED_OFF()      HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET)
#define TOGGLE_LED()       HAL_GPIO_TogglePin(LED_PORT, LED_PIN)
#define LED_IS_ON()        (HAL_GPIO_ReadPin(LED_PORT, LED_PIN) == GPIO_PIN_SET)

#define F407VE_FLASH_SIZE   0x0007FFFF
#define APP_START_ADDR      0x08020000
#define APP_END_ADDR        0x0807FFFF
#define BOOTLOADER_SIZE     0x10000      // 64KB bootloader空间
#define CPU_ID_BASE_ADDR (uint32_t*)(0x1FFF7A10)
#define FLASH_PAGE_SIZE     128  // STM32F407 Flash编程最小单位为128位(16字节)
#define HASH_SIZE           32
const char user_local_key[] = "Your_Password";
char cid_filename[48] = {0};
char cid_content[48] = {0};
char bin_filename[48] = {0};

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
uint32_t chipID[3];
uint8_t decryptionKey[HASH_SIZE];
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

FATFS fs;  // FatFs文件系统对象
FIL file;  // 文件对象

FIL InfoFile;
FIL AuthFile;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

int fputc(int ch, FILE *f) {
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1);
    HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 1);
    HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 1);
    return ch;
}

void Enable_RDP_Level1(void) {
    // 1. 解锁Flash和选项字节
    HAL_FLASH_Unlock();
    HAL_FLASH_OB_Unlock();

    // 2. 配置RDP Level 1
    FLASH_OBProgramInitTypeDef ob_config;
    HAL_FLASHEx_OBGetConfig(&ob_config);  // 获取当前配置(可选)
    ob_config.OptionType = OPTIONBYTE_RDP;
    ob_config.RDPLevel   = OB_RDP_LEVEL_1;

    // 3. 应用配置
    if (HAL_FLASHEx_OBProgram(&ob_config) == HAL_OK) {
        HAL_FLASH_OB_Launch();  // 触发重载
    }

    // 4. 重新锁定
    HAL_FLASH_OB_Lock();
    HAL_FLASH_Lock();
}

void Check_ReadProtection(void) {
    FLASH_OBProgramInitTypeDef obConfig;
    HAL_FLASHEx_OBGetConfig(&obConfig);

    if (obConfig.RDPLevel == OB_RDP_LEVEL_0)
    {
        // 读保护未启用,则手动开启
      Enable_RDP_Level1();
    }
}

/**
  * @brief  根据地址获取扇区号
  * @param  address: Flash地址
  * @retval 扇区号 (0-11)
  */
static uint32_t Get_Sector(uint32_t address)
{
    uint32_t sector = 0;
    
    if((address < FLASH_BASE) || (address > FLASH_BASE + F407VE_FLASH_SIZE)) {
        Error_Handler();
    }
    
    address -= FLASH_BASE; // 转换为偏移地址
    
    if(address < 0x10000) {
        // 前4个扇区各16KB
        sector = address / 0x4000;
    } else if(address < 0x20000) {
        sector = FLASH_SECTOR_4; // 64KB扇区
    } else {
        // 剩余128KB扇区 (Sector5-11)
        sector = ((address - 0x20000) / 0x20000) + FLASH_SECTOR_5;
    }
    
    return sector;
}

/**
  * @brief  擦除应用程序区域
  * @retval HAL status: HAL_OK 成功, HAL_ERROR 失败
  */
HAL_StatusTypeDef Erase_Application_Sectors(void)
{
    FLASH_EraseInitTypeDef erase;
    uint32_t sector_error = 0;
    HAL_StatusTypeDef status;
    
    // 解锁Flash
    HAL_FLASH_Unlock();
    
    // 清除所有错误标志
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 
                          FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
    
    // 计算需要擦除的扇区范围
    uint32_t start_sector = Get_Sector(APP_START_ADDR);
    uint32_t end_sector = Get_Sector(APP_END_ADDR);
    
    // 设置擦除参数
    erase.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase.Banks = FLASH_BANK_1;  // F407只有Bank1
    erase.Sector = start_sector;
    erase.NbSectors = end_sector - start_sector + 1;
    erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 适用于3.3V
    
    // 执行扇区擦除
    status = HAL_FLASHEx_Erase(&erase, &sector_error);
    
    // 锁定Flash
    HAL_FLASH_Lock();
    
    return status;
}

void mount_file_system()
{
    /* 挂载文件系统 */
    FRESULT res;
    const TCHAR* SDPath = "0:";  // SD 卡路径
    res = f_mount(&SDFatFS, SDPath, 1);
    if (res != FR_OK)
    {
        printf("Bootloader TF Card Mount Failed! f_mount error code: %d\r\n", res);
        Error_Handler();
    }
    else
    {
        printf("Bootloader TF Card Mount success\r\n");
    }
}

void get_chipid(uint8_t* id_buf) {
    uint32_t* id_ptr = (uint32_t*)CPU_ID_BASE_ADDR;
    memcpy(id_buf, id_ptr, 12);
}

// 生成加密密钥
void generate_encryption_key(const uint32_t* chipID, const char* userKey, uint8_t* encryptionKey) {
    SHA256_CTX ctx;
    sha256_init(&ctx);
    sha256_update(&ctx, (const uint8_t*)chipID, sizeof(uint32_t) * 3);
    sha256_update(&ctx, (const uint8_t*)userKey, strlen(userKey));
    sha256_final(&ctx, encryptionKey);
}

void FormatIDString(uint32_t *id, char *output) {
    sprintf(output, "0x%08X-0x%08X-0x%08X", id[0], id[1], id[2]);
}

ErrorStatus Get_File_Name()
{
    // 1. 获取芯片唯一ID
    get_chipid((uint8_t *)chipID);
    
    FormatIDString(chipID, cid_filename);
    strcat(cid_filename, ".cid");
    FormatIDString(chipID, bin_filename);
    strcat(bin_filename, ".bin");
    FormatIDString(chipID, cid_content);
    return SUCCESS;
}

FRESULT CreateChipIDFile() {
    FIL file;
    FRESULT fr;    
       
    UINT bytesWritten;
    fr = f_open(&file, cid_filename, FA_CREATE_ALWAYS | FA_WRITE);
    if (fr == FR_OK) {
        fr = f_write(&file, cid_content, 32, &bytesWritten);
        f_close(&file);
    } else {
        printf("CreateChipIDFile failed! Error: %d\n", fr);
    }
    
    if (bytesWritten == 32)
    {
        printf("CreateChipIDFile done!\r\n");
        return FR_OK; 
    }
    else
    {
        printf("CreateChipIDFile failed! code: %d\r\n", fr);
        return FR_DENIED;
    }
}

ErrorStatus DeleteFile() {

    FRESULT fr;
    fr = f_unlink(bin_filename);  // 删除文件
    if(fr != FR_OK) {
        printf("delete .bin Failed! Error: %d\r\n", fr);
        return ERROR;
    }
    fr = f_unlink(cid_filename);  // 删除文件
    if(fr != FR_OK) {
        printf("delete .cid Failed! Error: %d\r\n", fr);
        return ERROR;
    }
    f_mount(&SDFatFS, "", 0); // 卸载文件系统
    return SUCCESS;
}

ErrorStatus Check_Firmware_License()
{
    FIL file;
    FRESULT fr;
    UINT bytes_read;
    uint32_t file_size;
    
    fr = f_open(&file, bin_filename, FA_READ);
    if(fr != FR_OK) {
        printf("Open Firmware File Failed! Error: %d\r\n", fr);
        return ERROR;
    }
    
    // 3. 获取文件大小
    file_size = f_size(&file);
    if(file_size <= HASH_SIZE) {
        printf("Invalid firmware size: %d bytes\r\n", file_size);
        f_close(&file);
        return ERROR;
    }
    
    // 4. 生成解密密钥(SHA256)
    generate_encryption_key(chipID, user_local_key, decryptionKey); // 密钥生成函数
    
    // 5. 读取并解密许可证区域(前32字节)
    uint8_t stored_hash_enc[HASH_SIZE];
    fr = f_read(&file, stored_hash_enc, HASH_SIZE, &bytes_read);
    if(fr != FR_OK || bytes_read != HASH_SIZE) {
        printf("Read license failed! Error: %d\r\n", fr);
        f_close(&file);
        return ERROR;
    }
    uint8_t stored_hash[HASH_SIZE];
    for(int i=0; i<HASH_SIZE; i++) {
        stored_hash[i] = stored_hash_enc[i] ^ decryptionKey[i];
    }
        
    // 初始化SHA-256上下文
    SHA256_CTX ctx;
    sha256_init(&ctx);

    // 读取并计算文件内容哈希(每次32字节),包括解密过程,每读取32字节都要先解密才能送入sha256
    uint8_t hash[HASH_SIZE];
    uint32_t bytes_processed = 0;
    uint32_t content_size = file_size - HASH_SIZE; // 实际内容大小(减去许可证)
    static UINT key_index = 0;
    
    while(bytes_processed < content_size) {
        UINT to_read = HASH_SIZE;
        if(content_size - bytes_processed < HASH_SIZE) {
            to_read = content_size - bytes_processed;
        }
        
        fr = f_read(&file, hash, to_read, &bytes_read);
        if(fr != FR_OK || bytes_read == 0) {
            printf("Read error at pos %d: %d\r\n", bytes_processed, fr);
            f_close(&file);
            return ERROR;
        }
        
        // 解密数据块(按字节异或)
        for(UINT i=0; i<bytes_read; i++) {
            hash[i] ^= decryptionKey[key_index];
            key_index = (key_index + 1) % HASH_SIZE; // 循环使用密钥
        }
        
        sha256_update(&ctx, hash, bytes_read);
        bytes_processed += bytes_read;
    }
    
    // 获取最终哈希值
    uint8_t calculated_hash[HASH_SIZE];
    sha256_final(&ctx, calculated_hash);
    
    // 关闭文件
    f_close(&file);
    
    
    // 比较哈希值
    uint8_t diff = 0;
    for(int i = 0; i < HASH_SIZE; i++) {
        diff |= stored_hash[i] ^ calculated_hash[i];
    }
    ErrorStatus hash_valid = (diff == 0) ? SUCCESS : ERROR;
        
    if(!hash_valid) {
        // 处理许可证无效的情况
        printf("Firmware license verification failed!\n");
        printf("Take appropriate action: halt system, use safe mode, or notify administrator.\n");
        return ERROR;
    }
    return SUCCESS;
}

/**
  * @brief  修复版SD卡固件烧录函数
  * @retval HAL_OK 成功, HAL_ERROR 失败
  */
HAL_StatusTypeDef Update_Firmware_From_SD(void)
{
    FRESULT fr;
    UINT bytes_read;
    uint32_t total_words = 0;  // 改为按字计数
    uint8_t buffer[512];      // 512字节缓冲区
    uint8_t tail_buffer[4];   // 用于处理文件尾部的非完整字
    uint8_t tail_count = 0;
    static UINT key_index = 0;
    
    // 打开固件文件
    fr = f_open(&file, bin_filename, FA_READ);
    if(fr != FR_OK) {
        printf("Open Frimware File Failed!\r\n");
        return HAL_ERROR;
    }
    // 先读取32字节的hash值,将文件指针跳过32字节
    fr = f_read(&file, buffer, HASH_SIZE, &bytes_read);
    if(fr != FR_OK || bytes_read != HASH_SIZE) {
        return HAL_ERROR;
    }
    
    // 解锁Flash
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 
                          FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
    
    
    // 开始编程
    while(1) {
        // 处理上一块剩余的字节(如果有)
        if(tail_count > 0) {
            // 读取新数据,补齐4字节
            UINT bytes_to_read = 4 - tail_count;
            fr = f_read(&file, tail_buffer + tail_count, bytes_to_read, &bytes_read);
            
            if(fr != FR_OK || bytes_read == 0) {
                // 文件已结束,但还有部分数据需要写入
                if(tail_count > 0) {
                    // 填充剩余字节为0xFF
                    for(int i = tail_count; i < 4; i++) {
                        tail_buffer[i] = 0xFF;
                    }
                    // 写入最后一个不完整的字
                    uint32_t tail_data;
                    memcpy(&tail_data, tail_buffer, 4);
                    if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 
                                         APP_START_ADDR + total_words * 4, 
                                         tail_data) != HAL_OK) {
                        goto update_error;
                    }
                    total_words++;
                }
                break;
            }
            
            // 现在有完整的4字节数据
            uint32_t tail_data;
            memcpy(&tail_data, tail_buffer, 4);
            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 
                                 APP_START_ADDR + total_words * 4, 
                                 tail_data) != HAL_OK) {
                goto update_error;
            }
            total_words++;
            tail_count = 0;
        }
        
        // 读取文件数据
        fr = f_read(&file, buffer, sizeof(buffer), &bytes_read);
        if(fr != FR_OK || bytes_read == 0) {
            break;
        }
        
        // 解密数据块(按字节异或)
        for(UINT i=0; i<bytes_read; i++) {
            buffer[i] ^= decryptionKey[key_index];
            key_index = (key_index + 1) % HASH_SIZE; // 循环使用密钥
        }
        
        // 处理完整的4字节块
        uint32_t full_words = bytes_read / 4;
        for(uint32_t i = 0; i < full_words; i++) {
            uint32_t addr = APP_START_ADDR + total_words * 4;
            uint32_t data;
            memcpy(&data, &buffer[i * 4], 4);
            
            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data) != HAL_OK) {
                goto update_error;
            }
            total_words++;
        }
        
        // 处理剩余的不完整字
        tail_count = bytes_read % 4;
        if(tail_count > 0) {
            memcpy(tail_buffer, &buffer[full_words * 4], tail_count);
        }
    }
    
    // 关闭文件
    f_close(&file);
    
    // 锁定Flash
    HAL_FLASH_Lock();
    
    return HAL_OK;
    
update_error:
    f_close(&file);
    HAL_FLASH_Lock();
    return HAL_ERROR;
}


/**
  * @brief  跳转到应用程序
  */
void Jump_To_Application()
{
//    for (int i = 0; i < 16; i++) {
//        NVIC_DisableIRQ((IRQn_Type)i);
//    }
    
    if(((*(uint32_t*)APP_START_ADDR) & 0x2FF00000) != 0x20000000) {
        return;
    }
    
    SCB->VTOR = APP_START_ADDR;
    
//    // 关闭所有中断
//    __disable_irq();
    
    __set_MSP(*(__IO uint32_t*)APP_START_ADDR);
    
    typedef void (*pFunction)(void);
    pFunction app_entry;
    app_entry = (pFunction)(*(__IO uint32_t*)(APP_START_ADDR + 4));
    
    app_entry();
    while(1);
}

/**
  * @brief  主更新流程
  */
ErrorStatus Perform_Firmware_Update(void)
{
    mount_file_system();
    
    Get_File_Name();
    
    if (Check_Firmware_License() == ERROR)
    {
        printf("Check_Firmware_License Error!\r\n");
        printf("Please Retry\r\n");
        Error_Handler();
        return ERROR;
    }
    
    if(Erase_Application_Sectors() != HAL_OK) {
        printf("Erase App Sectors Failed!\r\n");
        Error_Handler();
        return ERROR;
    }
    printf("Erase_Application_Sectors done!\r\n");
    
    if(Update_Firmware_From_SD() != HAL_OK) {
        printf("Update Frimware Failed!\r\n");
        Error_Handler();
        return ERROR;
    }
    
    DeleteFile();
    
    return SUCCESS;
}

void delay_ms(uint32_t ms)
{
    const uint32_t hsi_freq = 16000000; 
    
    volatile uint32_t cycles = ms * (hsi_freq / 6000);
    
    while(cycles--);
}

/*
* 时序检测:上电时刻如果检测到1,在3~5s内检测到下降沿,则成功进入升级模式
* 
*/
ErrorStatus Check_Firmware_Upgrade()
{
    if (READ_MODE_PIN() == GPIO_PIN_RESET)
    {
        return ERROR;
    }
    delay_ms(3000);
    if (READ_MODE_PIN() == GPIO_PIN_RESET)
    {
        return ERROR;
    }
    delay_ms(2000);
    if (READ_MODE_PIN() == GPIO_PIN_RESET)
    {
        return SUCCESS;
    }
    return ERROR;
}

void Succeed_Light()
{
    while (1) {
        const uint16_t cycle = 8;      // 缩短PWM周期至8ms(125Hz)
        const uint16_t steps = 80;     // 减少呼吸阶段步数
        const uint16_t min_step = 2;   // 设置最小亮度阈值
        
        // 渐暗阶段优化
        for (uint16_t i = steps; i > min_step; i--) {
            uint16_t on_time = ((uint32_t)cycle * i * i) / (steps * steps); // 二次曲线加快变化速度
            
            SET_LED_ON();
            if(on_time > 0) HAL_Delay(on_time);
            
            SET_LED_OFF();
            uint16_t off_time = cycle - on_time;
            if(off_time > 0) HAL_Delay(off_time);
        }
        
        // 渐亮阶段优化
        for (uint16_t i = min_step; i < steps; i++) {
            uint16_t on_time = ((uint32_t)cycle * i * i) / (steps * steps); // 二次曲线加快变化速度
            
            SET_LED_ON();
            if(on_time > 0) HAL_Delay(on_time);
            
            SET_LED_OFF();
            uint16_t off_time = cycle - on_time;
            if(off_time > 0) HAL_Delay(off_time);
        }
    }
}

void Failed_Light()
{
    for (uint16_t i = 0; i < 10; i++) {
        TOGGLE_LED();
        HAL_Delay(200);
    }
    SET_LED_OFF();
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */
  // enable memory read protection.
  Check_ReadProtection();
  
  MX_GPIO_Init();
  if (Check_Firmware_Upgrade() == ERROR)
  {
      Jump_To_Application();
      for (uint16_t i = 0; i < 10; i++) {
          TOGGLE_LED();
          delay_ms(200);
      }
      SET_LED_OFF();
      while(1);
  }
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_SDIO_SD_Init();
  MX_FATFS_Init();
  MX_USART2_UART_Init();
  MX_USART3_UART_Init();
  /* USER CODE BEGIN 2 */
  setvbuf(stdout, NULL, _IONBF, 0);
  printf("Start to Upgrade Firmware\r\n");
  if (Perform_Firmware_Update() == SUCCESS)
  {
      printf("Perform_Firmware_Update done!\r\n");
      printf("Please Restart the Device\r\n");
      Succeed_Light();
  }
  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  CreateChipIDFile();
  Failed_Light();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

网站公告

今日签到

点亮在社区的每一天
去签到