基于GD32W51x MCU Trust Zone安全机制学习总结

GD32W51x 为国内领先的半导体厂商兆易创新最新发布的一款基于Cotrex-M33内核的MCU,Cortex-M33内核基于Armv8-M指令集架构,集成了TrustZone硬件安全机制,支持独立的存储访问空间,提供了系统开发所必需的安全性和灵活性。本文主要基于GD32W51x MCU 介绍TrustZone安全机制的内容及具体工程应用方法。
1.1 Trust Zone介绍
TrustZone的本质是在一颗CPU上隔出两个世界,即安全和非安全世界。我们可以将系统安全相关的硬件底层驱动代码、外设模块、数据放在安全世界中,可以防止非法的访问和数据篡改,将用户逻辑层代码放在非安全区。框图如下所示。
CPU 安全和非安全划分框图
每个外设模块、sram、flash都有自己的安全和非安全访问地址。外设模块的安全/非安全属性由TZPCU_TZSPC_SAM_CFG0~CFG3寄存器配置,sram的安全/非安全属性由TZBMPC0 ~TZBMPC3寄存器配置,flash 的安全/非安全地址范围由FMC_SECMCFG0寄存器配置。MCU内存地址的安全属性由SAU(安全属性单元)和IDAU(实施定义属性单元)来定义。内存映射安全属性与 SAU 配置区域的示例如下图所示。
SAU和IDAU配置示例图
IDAU 预定义了一个固定的非安全(NS)和非安全可调用(NSC)安全属性划分,IDAU 通过硬件实现了不可配置的固定内存映射分区,即上电后默认不可更改的。而 SAU 可以通过软件更改内存的安全属性,一个内存地址的安全性属性由 IDAU 和 SAU 一起决定,如果一个内存地址的安全属性在这两者的设定不一样,则会采用较高安全等级的属性(S>NSC>NS)。SAU共有8个region区域可以配置,上图由下到上分别为region0 ~ region7 区域可配置的属性。NSC为安全区域非安全可调区域,为了开发的方便性,两个世界之间是可以任意切换的,安全世界可以访问非安全世界的资源,而非安全世界是不能随意调用安全世界的资源。那该如何呢?所以NSC区域的作用就体现了,NSC为安全地址范围内定义的一段非安全可调区域,如果安全世界的某些资源想让非安全世界调用,则可以将这些资源定义在NSC区域。
简单理解NSC区域就是从非安全区域访问安全区域某些资源的唯一 通道。S、NSC、NS三者之间调用关系框图如下所示。
S/NSC/NS三者调用框图
TrustZone安全机制在MCU的软件开发模型如下图所示。
TrustZone软件开发模型
两个工程分别为安全工程和非安全工程,下载时首先将非安全工程的代码下载到MCU非安全区域,然后再将安全工程的代码下载到MCU安全区域。因为在TrustZone安全机制使能的情况下,MCU总是先从安全地址启动,所以如果先下载安全程序时,在安全区跳转到非安全区,PC指针跳转的非安全区域flash地址并无代码,可能会造成程序跑飞。故建议下载时首先下载非安全区的工程代码。

1.2 TrustZone 具体项目应用
GD32W515x MCU默认TZEN=0,即TrustZone安全机制失能,此时的MCU上电后总是从非安全地址启动,和不带TrustZone安全机制的MCU的使用方法并无区别。如果我们要让TrustZone安全机制使能,首先得让TZEN=1。查阅GD的官方手册可知,要使TZEN=1,有2种方法,一是通过操作选项字节,二是通过操作EFUSE寄存器。个人建议采用第一种选项字节的方法,因为EFUSE寄存器只可写入一次,后面无法更改,一旦写入后,这颗芯片后面只能作为TrustZone安全机制芯片使用,上电从安全地址启动,不能再回到从非安全地址启动状态了。选项字节的编程方法可以参考GD官方用户手册FMC章节,具体的代码实现如下所示:

void TZEN_Enable(void)
{
	fmc_unlock();//首先FMC寄存器解锁
	while(fmc_flag_get(FMC_FLAG_BUSY)==SET);//等待FMC busy flag清0
	ob_unlock();//解锁FMC_CTL寄存器中的选项字节操作位
	while(((FMC_CTL & FMC_CTL_OBWEN)==RESET));//等待OBWEN置1
	while(ob_trustzone_enable()!=FMC_READY);//等待TZEN=1操作完成
	ob_lock();//选项字节上锁
	fmc_lock();//FMC寄存器上锁
}

本文使用的是GD 官方提供的TrustZone安全工程,后面的分析也是基于这个工程。当将上述代码下载到mcu中即可将TZEN=1。我们首先要确认TZEN是否已置1,将非安全和安全工程的代码下载到MCU中,通过JTAG进入Debug模式,看PC指针和SP指针的初始位置是否在安全地址,如果为安全地址则TZEN置1成功。
将TZEN置1后,下面通过SAU来配置MCU内存地址的安全属性,因为SAU配置与ARMv8架构下内核操作相关,为了方便开发,GD官方提供了一个配置文件partition_gd32w51x.h,我们将它拉进keil工程,就可以很方便的对SAU进行配置了。配置界面如下图所示:
SAU配置
TZEN=1时,MCU上电所有的外设、flash、sram都是默认安全的,所以我们需要在安全工程中配置非安全工程中的flash、sram属性为非安全。通过上图SAU配置可知,region0被配置为NSC区域,region1被配置为非安全区flash地址范围,region2被配置为非安全区sram地址范围,region3被配置为所有外设属性非安全可寻址范围。将SAU配置完成后,我们还需要修改安全工程和非安全工程的分散加载文件,使代码的加载和运行地址与我们配置的相同。
安全工程的分散加载文件如下:

LR_IROM1 0x0C000000 0x00040000 { ; load region size_region
ER_IROM1 0x0C000000 0x0003E000 { ; load address = execution address
*.o (RESET, +First)
*(InRootKaTeX parse error: Expected 'EOF', got '}' at position 41: … .ANY (+XO) }̲ RW_IRAM2 0x3…CMSE) ; check with partition.h
}
}

LR_IROM2 为NSC的地址范围,0x0C03E000为NSC 内存的起始地址,0x00002000为NSC区域的大小。RW_IRAM2 为安全地址sram的内存地址范围,通过查看GD官方手册存储器地址映射表可知为sram0。通过SAU配置可知非安全地址的flash空间0x08040000~0x081FFFFF,
起始地址相对于0x08000000地址偏移了40000,故可知安全地址的flash空间范围大小为64k(0x40000)。
非安全工程的分散加载文件如下所示:
LR_IROM1 0x08040000 0x001C0000 { ; load region size_region
ER_IROM1 0x08040000 0x001C0000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20010000 0x00010000 { ; RW data
.ANY (+RW +ZI)
}
}
LR_IROM1 为NS的地址范围,0x08040000为NS内存的起始地址,0x001C0000 为NS区域的大小。RW_IRAM1 为非安全地址sram的内存地址范围,通过查看GD官方手册存储器地址映射表可知为sram1,大小为64k。
修改完分散加载文件后,就要通过fmc寄存器安全标记功能配置flash的安全地址范围,调用函数如下:

void ob_secmark_config(0x00, 0x3F, SECM_INDEX0);//安全地址的起始页为0x00,结束页为0x3F,每页的大小为1k,故安全flash地址的大小为64k

通过TZBMPC0寄存器配置sram1的所有地址空间属性为非安全,配置代码如下:

    uint16_t block_number = 0U;
    /* enable TZPCU clock */
    rcu_periph_clock_enable(RCU_TZPCU);

    /* SRAM1 is used to nonsecure code, so all blocks of SRAM1 should set to nonsecure */
    for(block_number = 0U; block_number <= TZBMPC1_BLOCK_NUMBER; block_number++){
        tzpcu_tzbmpc_block_secure_access_mode_config(TZBMPC1, block_number, BLOCK_SECURE_ACCESS_MODE_NSEC);
    }

以上TrustZone的基本配置已完成,下面是在安全工程中实现非安全区域的跳转函数,主要是PC指针指向非安全地址的PC指针起始地址,SP指针指向非安全地址的栈顶位置,具体函数分析可参考我上一篇IAP设计思想实现博客。代码实现如下:

   uint32_t nonsecure_stackpointer = (*((uint32_t *)(NONSECURE_START)));
    ns_fptr nonsecure_reset_handler = (ns_fptr)(*((uint32_t *)(NONSECURE_START + 4U)));
    /* set non-secure vector table location */
    SCB_NS->VTOR = NONSECURE_START;
    /* set non-secure stack pointer */
    __TZ_set_MSP_NS(nonsecure_stackpointer);
    /* start non-secure application */
    nonsecure_reset_handler();

综上所述,基于基于GD32W51x MCU Trust Zone安全机制项目应用基本完成,进入Debug后,我们在非安全工程的main函数起始部分打断点,然后全速运行,可以看到程序可以运行到非安全工程且PC指针和SP指针的位置均指向非安全地址。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>