大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是飛思卡爾Kinetis系列MCU的KBOOT之ROM API特性。
KBOOT的ROM API特性主要存在於ROM Bootloader形態中,KBOOT內部整合了一些Kinetis內部IP模組driver,這些IP模組driver首要目的是用於實現KBOOT的功能,但由於這些IP模組driver會隨著KBOOT一起被固化在ROM空間裡,所以如果這些IP driver能夠被外部(主要是執行於Flash中的Application)呼叫,那麼肯定會節省Application程式碼空間,這麼看起來將ROM Bootloader裡的一些IP driver以API的形式export出去是很有意義的,這麼有意義的事,KBOOT當然是會做的。下面痞子衡給大家介紹KBOOT裡的ROM API特性:
一、API tree原型
KBOOT中宣告瞭如下一個名叫bootloader_tree_t的結構體來組織那些可被export出去的IP模組driver,結構體前4個成員記錄了版本、版權以及Bootloader等資訊,從第5個成員開始便是那些IP模組driver API,目前支援匯出的模組API並不多,一共4個:Flash、AES、Kboot、USB。
//! @brief Structure of version property.
typedef union StandardVersion
{
struct {
uint8_t bugfix; //!< bugfix version [7:0]
uint8_t minor; //!< minor version [15:8]
uint8_t major; //!< major version [23:16]
char name; //!< name [31:24]
};
uint32_t version; //!< combined version numbers
} standard_version_t;
//! @brief Root of the bootloader API tree.
//!
//! An instance of this struct resides in read-only memory in the bootloader. It
//! provides a user application access to APIs exported by the bootloader.
//!
//! @note The order of existing fields must not be changed.
typedef struct BootloaderTree
{
void (*runBootloader)(void *arg); //!< Function to start the bootloader executing.
standard_version_t version; //!< Bootloader version number.
const char *copyright; //!< Copyright string.
const bootloader_context_t *runtimeContext; //!< Pointer to the bootloader's runtime context.
const flash_driver_interface_t *flashDriver; //!< Flash driver API.
const aes_driver_interface_t *aesDriver; //!< AES driver API.
const kb_interface_t *kbApi; //!< Bootloader API.
const usb_driver_interface_t *usbDriver; //!< USB driver API.
} bootloader_tree_t;
在所有匯出的模組driver API中,Flash driver API是使用最廣泛的,其API原型如下(不同晶片中Flash driver API版本可能不一致,截止目前共有3個版本:F1.0.x、F1.1.x、F1.2.x):
//! @brief Interface for the flash driver.
typedef struct FlashDriverInterface {
#if !defined(FLASH_API_TREE_1_0)
standard_version_t version; //!< flash driver API version number.
#endif
status_t (*flash_init)(flash_driver_t * driver);
#if defined(FLASH_API_TREE_1_0)
status_t (*flash_erase_all)(flash_config_t *config);
status_t (*flash_erase_all_unsecure)(flash_config_t *config);
status_t (*flash_erase)(flash_config_t *config, uint32_t start, uint32_t lengthInBytes);
#else
status_t (*flash_erase_all)(flash_config_t *config, uint32_t key);
status_t (*flash_erase_all_unsecure)(flash_config_t *config, uint32_t key);
status_t (*flash_erase)(flash_config_t *config, uint32_t start, uint32_t lengthInBytes, uint32_t key);
#endif
status_t (*flash_program)(flash_driver_t * driver, uint32_t start, uint32_t * src, uint32_t lengthInBytes);
status_t (*flash_get_security_state)(flash_driver_t * driver, flash_security_state_t * state);
status_t (*flash_security_bypass)(flash_driver_t * driver, const uint8_t * backdoorKey);
status_t (*flash_verify_erase_all)(flash_driver_t * driver, flash_margin_value_t margin);
status_t (*flash_verify_erase)(flash_driver_t * driver, uint32_t start, uint32_t lengthInBytes, flash_margin_value_t margin);
status_t (*flash_verify_program)(flash_driver_t * driver, uint32_t start, uint32_t lengthInBytes, const uint8_t * expectedData, flash_margin_value_t margin, uint32_t * failedAddress, uint32_t * failedData);
status_t (*flash_get_property)(flash_driver_t * driver, flash_property_t whichProperty, uint32_t * value);
#if (!defined(FLASH_API_TREE_1_0)) && (!defined(FLASH_API_TREE_1_1))
status_t (*flash_register_callback)(flash_driver_t * driver, flash_callback_t callback);
status_t (*flash_program_once)(flash_driver_t * driver, uint32_t index, uint32_t * src, uint32_t lengthInBytes);
status_t (*flash_read_once)(flash_driver_t * driver, uint32_t index, uint32_t * dst, uint32_t lengthInBytes);
status_t (*flash_read_resource)(flash_driver_t * driver, uint32_t start, uint32_t *dst, uint32_t lengthInBytes, flash_read_resource_option_t option);
#endif
} flash_driver_interface_t;
下表列出了所有含ROM空間的晶片中Flash driver API的版本:
- Note: 模組driver API設計必須滿足幾個條件:一、API裡不能使用全域性變數;二、模組IRQHandler不能直接當做API
二、API tree位置
宣告好了bootloader_tree_t結構體原型以及各IP模組driver API原型,下一步便是在KBOOT中定義如下常量g_bootloaderTree以建立物件分配記憶體(不同晶片中g_bootloaderTree版本可能不一致,下面是用於K80晶片上的K1.3.0版)。
//! @brief Static API tree.
const bootloader_tree_t g_bootloaderTree =
{
.runBootloader = bootloader_user_entry,
.version = {
.name = kBootloader_Version_Name,
.major = kBootloader_Version_Major,
.minor = kBootloader_Version_Minor,
.bugfix = kBootloader_Version_Bugfix
},
.copyright = bootloaderCopyright,
.runtimeContext = &g_bootloaderContext,
.flashDriver = &g_flashDriverInterface,
.aesDriver = &g_aesInterface
};
只要找到g_bootloaderTree地址,便可以訪問到那些IP模組driver API,現在的問題是如何找到g_bootloaderTree地址?我們知道在KBOOT工程中,如果不在連結檔案裡明確指定g_bootloaderTree地址,連結器會隨機分配一個地址來存放g_bootloaderTree,這會導致在不同晶片中g_bootloaderTree地址是不一樣的;但即使強制指定g_bootloaderTree連結地址,如果ROM空間起始地址不一定是從0x1c000000開始,那麼還是難以做到g_bootloaderTree地址統一。到底該怎麼解決這個問題?KBOOT使用了一個巧妙的方法,下面是KBOOT工程的startup檔案(IAR版),KBOOT將g_bootloaderTree的地址放到了中斷向量表第8個向量的位置處(該向量為ARM Cortex-M未定義的系統向量),因此只要知道了ROM空間的起始地址,那麼偏移0x1c處開始的4bytes便是g_bootloaderTree地址。
MODULE ?cstartup
;; Forward declaration of sections.
SECTION CSTACK:DATA:NOROOT(3)
SECTION .intvec:CODE:NOROOT(2)
EXTERN g_bootloaderTree
PUBLIC __vector_table
PUBLIC __vector_table_0x1c
DATA
__vector_table
DCD sfe(CSTACK)
DCD Reset_Handler
DCD NMI_Handler
DCD HardFault_Handler
DCD MemManage_Handler
DCD BusFault_Handler
DCD UsageFault_Handler
__vector_table_0x1c
DCD g_bootloaderTree
DCD 0
DCD 0
DCD 0
DCD SVC_Handler
DCD DebugMon_Handler
DCD 0
DCD PendSV_Handler
DCD SysTick_Handler
;; ...
三、呼叫API的方法
KBOOT中的ROM API呼叫方法非常簡單,以K80晶片中Flash driver API呼叫為例,首先需要按以下步驟定義s_flashInterface指標:
#define BOOTLOADER_TREE_LOCATION (0x1c00001cul)
#define BOOTLOADER_API_TREE_POINTER (*(bootloader_tree_t **)BOOTLOADER_TREE_LOCATION)
static const flash_driver_interface_t *s_flashInterface = BOOTLOADER_API_TREE_POINTER->flashDriver;
有了s_flashInterface指標便可以隨意訪問Flash driver API:
const uint32_t test_program_buffer[2] = { 0x01234567, 0x89abcdef };
flash_config_t flashInstance;
s_flashInterface->flash_init(&flashInstance);
s_flashInterface->flash_erase(&flashInstance, 0x8000, 0x1000);
s_flashInterface->flash_program(&flashInstance, 0x8000, (uint32_t *)test_program_buffer, 8);
至此,飛思卡爾Kinetis系列MCU的KBOOT之ROM API特性痞子衡便介紹完畢了,掌聲在哪裡~~~