GD32篇 记录
一、GD32开发版程序设置读保护,防止程序被读取
平台:PC 系统:Win10 1909 开发版:GD32F103RCT6 软件:Keil、STM32 ST-LINK Utility
STM32 ST-LINK Utility下载链接:密码2022
前言
在实际发布的产品中,在GD32芯片的内部FLASH存储了控制程序,如果不作任何保护措施的话,可以使用下载器直接把内部FLASH的内容读取回来,得到bin或hex文件格式的代码拷贝,别有用心的厂商会利用该方法山寨产品。 为了解决这个问题,GD32芯片提供了好几种保护内部Flash不被轻易读取的方式,但是一般在默认的情况下这个功能是没有打开的。
一、STM32 ST-LINK Utility?
STM32 ST-LINK Utility这个软件工具其实主要就是配套“ST-LINK”这个下载工具一起使用的上位机软件。因此使用STM32 ST-LINK Utility上位机软件需要有一个ST-LINK工具才行。它的功能和J-Link对应的工具类似,用于烧写代码。
STM32 ST-LINK Utility工具在产品开发过程中测试一些其他版本的代码,可以直接下载hex,而不用打开工程再编译去下载。 当你开发完一个STM32产品,需要量产的时候,就可以用这个工具直接下载hex代码,对代码加密(读保护)
二、使用步骤
1.ST-LINK Utility界面介绍
安装下载完成之后打开就是这个界面
点击Target
接着点击Connect
出现一下信息就说明成功读取到了内部的Flash了
很明显,已经通过ST-Link用 STM32 ST-LINK Utility读取出来了Flash 的内容,我甚至还可以保存成BIN文件
2.写入数据
接下来往Flash里面写一些内容,测试一下
代码如下(示例):
const char FIRMWARE[] __attribute__((section(".ARM.__at_0x08010000"))) ={"WRITE FLASH"};
const char g_revision_datetime[]__attribute__((section(".ARM.__at_0x08010020"))) = __DATE__" " __TIME__;
在0x08010000这里写入“ WRITE FLASH” 在0x08010020 纪录编译代码时间
完成后编译代码,写入到芯片上,然后打开ST-LINK Utility 如下图
时间差不多 可以看到,用ST-link脸上GD32 的芯片,用ST-LINK Utility完全可以读出Flash的内容
三、GD32FMC介绍
可选项字节块编程
闪存结构
对于GD32F10x_MD,闪存页大小为1KB。 对于主存储闪存容量不多于512KB的GD32F10x_CL 和GD32F10x_HD,闪存页大小为2KB。例如RCT6 对于主存储闪存容量不少于768KB的GD32F10x_CL和 GD32F10x_XD,使用了两片闪存;前512KB容量在第一片闪存(bank0)中,后续的容量在第 二片闪存(bank1)中。其中bank0的闪存页大小为2KB,bank1的闪存页大小为4KB。主存储 闪存的每页都可以单独擦除。闪存结构见下图。
GD32F103RCT6属于GD32F10x_HD系列产品,每页的字节是2K,总共的Flash有256K,也就是0-127页
FMC相关的寄存器
#define FMC FMC_BASE
#define OB OB_BASE
#define FMC_WS REG32((FMC) + 0x00U)
#define FMC_KEY0 REG32((FMC) + 0x04U)
#define FMC_OBKEY REG32((FMC) + 0x08U)
#define FMC_STAT0 REG32((FMC) + 0x0CU)
#define FMC_CTL0 REG32((FMC) + 0x10U)
#define FMC_ADDR0 REG32((FMC) + 0x14U)
#define FMC_OBSTAT REG32((FMC) + 0x1CU)
#define FMC_WP REG32((FMC) + 0x20U)
#define FMC_KEY1 REG32((FMC) + 0x44U)
#define FMC_STAT1 REG32((FMC) + 0x4CU)
#define FMC_CTL1 REG32((FMC) + 0x50U)
#define FMC_ADDR1 REG32((FMC) + 0x54U)
#define FMC_WSEN REG32((FMC) + 0xFCU)
#define FMC_PID REG32((FMC) + 0x100U)
#define OB_SPC REG16((OB) + 0x00U)
#define OB_USER REG16((OB) + 0x02U)
#define OB_WP0 REG16((OB) + 0x08U)
#define OB_WP1 REG16((OB) + 0x0AU)
#define OB_WP2 REG16((OB) + 0x0CU)
#define OB_WP3 REG16((OB) + 0x0EU)
FMC_CTLx 寄存器解锁
复位后,FMC_CTLx寄存器进入锁定状态,LK位置为1。通过先后向FMC_KEY0寄存器写入 0x45670123和0xCDEF89AB,可以使得FMC_CTL0寄存器解锁。两次写操作后,FMC_CTL0 寄存器的LK位被硬件清0。可以通过软件设置FMC_CTL0寄存器的LK位为1再次锁定 FMC_CTL0寄存器。任何对FMC_KEY0寄存器的错误操作都会将LK位置1,从而锁定 FMC_CTL0寄存器,并引发一个总线错误。 FMC_CTL0寄存器的OBPG位和OBER位在FMC_CTL0寄存器第一层解锁后,仍然需要第二层 解锁。第二层解锁过程也是两次写操作,向FMC_OBKEY寄存器先后写入0x45670123和 0xCDEF89AB,然后硬件将FMC_CTL0寄存器的OBWEN位置1。软件可以将FMC_CTL0的 OBWEN位清0来锁定FMC_CTL0的OBPG位和OBER位。 对于GD32F10x_CL和GD32F10x_XD,FMC_CTL0寄存器用来设置对bank0和选项字节块的 操作,FMC_CTL1寄存器用来设置对bank1的擦写操作。FMC_CTL1的解锁和锁定机制和 FMC_CTL0类似。对FMC_KEY1写解锁序列可解除FMC_CTL1的锁定。
代码解释如下:
void fmc_unlock(void)
{
if((RESET != (FMC_CTL0 & FMC_CTL0_LK))){
FMC_KEY0 = UNLOCK_KEY0;
FMC_KEY0 = UNLOCK_KEY1;
}
if(FMC_BANK0_SIZE < FMC_SIZE){
if(RESET != (FMC_CTL1 & FMC_CTL1_LK)){
FMC_KEY1 = UNLOCK_KEY0;
FMC_KEY1 = UNLOCK_KEY1;
}
}
}
void ob_unlock(void)
{
if(RESET == (FMC_CTL0 & FMC_CTL0_OBWEN)){
FMC_OBKEY = UNLOCK_KEY0;
FMC_OBKEY = UNLOCK_KEY1;
}
FMC_CTL0 |= ~FMC_CTL0_OBWEN;
while(RESET == (FMC_CTL0 & FMC_CTL0_OBWEN)){
}
}
#define UNLOCK_KEY0 ((uint32_t)0x45670123U)
#define UNLOCK_KEY1 ((uint32_t)0xCDEF89ABU)
可选项字节块编程
FMC提供了一个32位整字/16位半字编程功能,可用来修改可选字节块内容。可选字节块共有 8对可选字节。每对可选字节的高字节是低字节的补。当低字节被修改时,FMC自动生成该选 项字节的高字节。字节块编程操作过程如下。 ? 确保FMC_CTL0寄存器不处于锁定状态; ? 等待FMC_STAT0寄存器的BUSY位变为0; ? 解锁FMC_CTL0寄存器的可选字节操作位; ? 等待FMC_CTL0寄存器的OBWEN位置1; ? 置位FMC_CTL0寄存器的OBPG位; ? DBUS写一个32位整字/16位半字到目的地址; ? 等待编程指令执行完毕,FMC_STAT寄存器的BUSY位清0; ? 如果需要,使用DBUS读并验证是否编程成功。 当可选字节块编程成功执行,FMC_STAT0寄存器的ENDF位置位。若FMC_CTL0寄存器的 ENDIE位被置1, FMC将触发一个中断。需要注意的是,执行整字/半字编程操作需要检查目 的地址是否已经被擦除。如果该地址没有被擦除,对该地址写一个非0x0值,FMC_STAT0寄存 器的PGERR位将被置1,对该地址的编程操作无效(当写内容为0x0时,即使目的地址没有被 正常擦除,也可以正确编程)。 当可选字节被改变时,需要系统复位使之生效。
可选字节块说明
每次系统复位后,闪存的可选字节块被重加载到FMC_OBSTAT和FMC_WP寄存器,可选字节 生效。可选字节的补字节具体为可选字节取反。当可选字节被重装载时,如果可选字节的补字 节和可选字节不匹配,FMC_OBSTAT寄存器的OBERR位将被置1,可选字节被强制设置为 0xFF。若可选字节和其补字节同为0xFF,则OBERR位不置位。可选字节详情见下表。
页擦除/编程保护
FMC的页擦除/编程保护功能可以阻止对闪存的意外操作。当FMC对被保护页进行页擦除或编 程操作时,操作本身无效且FMC_STAT寄存器的WPERR位将被置1。如果WPERR位被置1且 FMC_CTL寄存器的ERRIE位也被置1来使能相应的中断,FMC将触发闪存操作出错中断,等待 CPU处理。配置可选字节块的WP [31:0]某位为0可以单独使能某几页的保护功能。如果在可选 字节块执行了擦除操作,所有的闪存页擦除和编程保护功能都将失效。当可选字节的WP被改 变时,需要系统复位使之生效。
安全保护,也称读保护
FMC提供了一个安全保护功能来阻止非法读取闪存。此功能可以很好地保护软件和固件免受非 法的用户操作。
未保护状态:当将SPC字节和它的补字节被设置为0x5AA5,系统复位以后,闪存将处于非安 全保护状态。主存储块和可选字节块可以被所有操作模式访问
已保护状态:当设置SPC字节和它的补字节值为任何除0x5AA5外的值,系统复位以后,安全 保护状态生效。
需要注意的是,若该修改过程中,MCU的调试模块依然和外部JTAG/SWD设备 相连,需要用上电复位代替系统复位以使得修改后的保护状态生效。
在安全保护状态下,主存储闪存块仅能被用户代码访问且前4KB的闪存自动处于页擦除/编程保护状态下。在调试模式下, 或从SRAM中启动时,以及从boot loader区启动时,这些模式下对主存储块的操作都被禁止。
如果在这些模式下读主存储块,将产生总线错误。如果在这些模式下,对主存储块进行编程或 擦除操作,FMC_STAT寄存器的WPERR位将被置1。但这些模式下都可以对可选字节块进行操 作,从而可以通过该方式失能安全保护功能。如果将SPC字节和它的补字节设置为0x5AA5, 安全保护功能将失效,并自动触发一次整片擦除操作。
操作函数:fmc_state_enum ob_security_protection_config(uint8_t ob_spc)
函数原型如下:
fmc_state_enum ob_security_protection_config(uint8_t ob_spc)
{
fmc_state_enum fmc_state = fmc_bank0_ready_wait(FMC_TIMEOUT_COUNT);
if(FMC_READY == fmc_state){
FMC_CTL0 |= FMC_CTL0_OBER;
FMC_CTL0 |= FMC_CTL0_START;
fmc_state = fmc_bank0_ready_wait(FMC_TIMEOUT_COUNT);
if(FMC_READY == fmc_state){
FMC_CTL0 &= ~FMC_CTL0_OBER;
FMC_CTL0 |= FMC_CTL0_OBPG;
OB_SPC = (uint16_t)ob_spc;
fmc_state = fmc_bank0_ready_wait(FMC_TIMEOUT_COUNT);
if(FMC_TOERR != fmc_state){
FMC_CTL0 &= ~FMC_CTL0_OBPG;
}
}else{
if(FMC_TOERR != fmc_state){
FMC_CTL0 &= ~FMC_CTL0_OBER;
}
}
}
return fmc_state;
}
#define FMC_NSPC ((uint8_t)0xA5U)
#define FMC_USPC ((uint8_t)0xBBU)
使能读保护:
ob_security_protection_config(FMC_USPC);
失能读保护
ob_security_protection_config(FMC_NSPC);
如果解除了读保护,系统会自动全芯片擦除。
使能读保护
失能读保护之前最好先判断一下系统是否处于读保护状态
ob_spc_get()
FlagStatus ob_spc_get(void)
{
FlagStatus spc_state = RESET;
if(RESET != (FMC_OBSTAT & FMC_OBSTAT_SPC)){
spc_state = SET;
}else{
spc_state = RESET;
}
return spc_state;
}
操作代码
fmc_unlock();
ob_unlock();
ob_security_protection_config(FMC_USPC);
fmc_lock();
ob_lock();
NVIC_SystemReset();
fmc_unlock();
ob_unlock();
ob_security_protection_config(FMC_NSPC);
fmc_lock();
ob_lock();
效果
使能读保护后重新使用 ST-LINK Utility读取芯片 此时已经开始报错,无法读取Flash
使用ST-LINK Utility解除读保护
总结
记录于2022/09/27,以便以后方便找
|