TrustZone 用户编程模型(GPIO Toggle例子)

这里拿STM32L5的GPIO例子开刀,可以看到程序里面有两份,一份是安全环境里的,一份是非安全环境里的.他们有各自的startup,终端处理函数,main函数,以及不同的linker文件(图中看不到linker文件).

先编译安全环境代码(S),再编译非安全环境代码(NS),设置选项字中TZEN=1,DBANK=1,区域1起始0,结束0x7F,区域2起始1,结束0,然后分别下载他们,就能看到板子上两个LED闪烁.

问题,现在STM32L552ZE自带512K Flash和256K RAM,要划分出S,NS,NSC区域,又是怎么做到的.

首先,是否安全是由IADU和SAU共同作用规定的,SAU是用户可以自己配置的,刚上电时候,SAU规定全部内容都是安全的,所以,要修改配置,自然是要在安全Domain才能可以修改,所以复位第一件事,肯定就在安全Domain上.IDAU虽然不能修改,但是他给了一个重要的概念,定义了区域的别名.

访问0x2000 0000和访问0x3000 0000实际上访问同一个东西,只不过访问0x3000 0000时候,CPU看这块区域是安全的,访问0x2000 0000时,CPU看这块区域是不安全的.即虽然我们把代码下载到0x0C00 0000,但是实际储存位置依然是0x0800 0000的.

由于Map的原因,所以要记得主要这几个区域映射关系,不然开发的时候可能会比较费劲.

安全世界的代码,放在实际地址0开始的地方,而CPU要从0x0C00 0000去寻址,这样才能和当前的安全属性匹配得上(否则强制降级),就是0x0C00 0000开始的256K空间,从0x0C00 0000到0x0C03 FFFF.这就是映射带来的好处.非安全代码同理.

从上面这个截图看,这部分是安全部分代码,从0 – 0x3FFFF的Flash,以及0 – 0x18000的RAM都分配给安全部分.

而这个截图,是从0x40000 – 0x80000区域全占用,内存就使用剩下的160K.

首先,回到最初的说法,芯片复位时,程序还没有跑起来,默认的SAU又是全片处于安全模式,所以,在CPU看来,整个芯片都是安全模式的.还记得一开始设置过选项字么,这里马上就起作用.分割了部分安全,部分不安全.

看这个表,SAU上电设置安全,CPU怎么看都是安全,选项字设置了部分区域属于不安全,由于只要任意一个位置认为是安全,CPU看到就是安全.所以,CPU现在怎么看都觉得整个都是安全的.

现在进入安全固件的调试状态,输入0x08000000,0x0C000000,0x08040000,0x0C040000来看.发现前面两个都能正常(他们是Map的,所以看到的东西会一样),后两个怎么都返回0,已知这个不可能真的就是0,其实是因为安全属性不匹配.

只有transaction属性是匹配的才能访问!

代码继续执行,直到配置SAU之后.0x08040000就可以访问了,因为,属性匹配了.NS+NS=NS,NS可访问NS区域.执行这个配置的是TZ_SAU_Setup.STM32L5内置了8个设置区域.0x0C040000不能访问,因为不匹配.

内存是由GZTC来控制的,所以还要再运行一会.直到主函数的MX_GTZC_Init完成.

执行完之后吗,前三个区域合格,最后一个区域还是有问题.接下来还要分配其他资源,比如GPIO,现在已经做了不少事情,先理一下逻辑.

  1. 上电
  2. 受到OB影响,Flash上半部分安全,下半部分不安全.
  3. 配置SAU,让CPU知道不是所有地方都是安全模式的.区域根据之前的图可以看出,开始部分安全,后面部分不安全.
  4. 用GTZC配置SRAM哪些地方安全,哪些地方不安全.
  5. GPIO本身都是安全资源,配置所有IO(除了PC7,其他都是安全的),即代码中HAL_GPIO_ConfigPinAttributes(GPIOC, (GPIO_PIN_All & ~(GPIO_PIN_7)), GPIO_PIN_NSEC);
  6. 默认状态下所有中断都会target到安全模式,如果需要可以修改其target(不是所有中断都能retarget),比如SysTick就是banked的,目前在S和NS的世界里各自都有一个.当然,最初的初始化已经把安全模式下的SysTick设置成1ms中断.(和以前的STM32配置差不多)
  7. 安全世界的事情做完就交给非安全世界去做.

那怎么设置SAU的呢,这里有一个做好的文件.

这个文件还可以设置SCB_AIRCR_SYSRESETREQS_VAL和SCB_AIRCR_PRIS_VAL,决定某些敏感操作是否仅能安全模式使用.FPU配置以及中断是否target到非安全环境.暂时先不理解后续这些.

那现在马上要交给NS世界处理事情了.

强制进行跳转,剩下由硬件来完成就可以了.

static void NonSecure_Init(void)
{
  funcptr_NS NonSecure_ResetHandler;

  SCB_NS->VTOR = VTOR_TABLE_NS_START_ADDR;

  /* Set non-secure main stack (MSP_NS) */
  __TZ_set_MSP_NS((*(uint32_t *)VTOR_TABLE_NS_START_ADDR));

  /* Get non-secure reset handler */
  NonSecure_ResetHandler = (funcptr_NS)(*((uint32_t *)((VTOR_TABLE_NS_START_ADDR) + 4U)));

  /* Start non-secure state software application */
  NonSecure_ResetHandler();
}

现在换到NS工程来看.也进行SystemInit,只是NS的SystemInit只设置了VTOR,没有干任何东西.

临时回到S项目里看,函数SECURE_SystemCoreClockUpdate被CMSE_NS_ENTRY修饰,这个函数就会出现在NSC,就可以被NS调用.至于调用怎么清理环境,这个汇编器会帮我们搞定.

在NS代码中看到,SECURE_RegisterCallback注册了两个回调函数,其实SECURE_RegisterCallback本意设定,出现SecureFault_Handler和GTZC_IRQHandler,都是在S空间处理的,处理完回到NS空间.从S调用NS,用cmse_nsfptr_create,然后直接调用就可以.

剩下NS空间里就是LED2翻转的功能.

void HAL_SYSTICK_Callback(void)
{
  if (NonSecureTimingDelay != 0U)
  {
    NonSecureTimingDelay--;
  }
  else
  {
    /* Toggle PB.07 (LED2) */
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
    NonSecureTimingDelay = NONSECURE_IO_TOGGLE_DELAY;
  }
}

LED1也是通过类似方法,在S世界翻转的.

发表评论

电子邮件地址不会被公开。 必填项已用*标注