MPC 与 PPC 配置

在 TrustZone 系统中,安全边界不仅存在于代码执行层面,还需要在硬件总线层面进行隔离保护。 MPC 负责对物理内存区域进行安全属性划分,PPC 负责对外设进行安全属性控制。两者在总线从设备端实时拦截非法访问请求,确保安全资源不会被非安全主设备或非安全代码窃取或篡改。

在实际开发中,开发者经常需要调整内存区域或外设的安全属性。例如,在安全世界与非安全世界之间共享一块内存缓冲区,或将某个 I2C 接口独占给安全世界使用。本节将介绍 MPC 和 PPC 的配置方法。

MPC 配置

业务场景:跨世界共享内存数据

假设客户在 SRAM 特定区域中划定了一块 8KB 的缓存区,用于存放非安全端采集到的数据包,随后交由安全端解密。 为了让非安全侧的 OS 和 DMA 能够顺利将数据搬运到这块内存而不触发总线异常(Bus Fault),我们需要将这块特定内存的访问权限从"仅安全读写"放宽为"非安全可读写"。

底层机制与原理

MPC 负责为物理内存区域划分安全属性。它在总线层面实时拦截非法访问:如果是安全内存,非安全主设备的读写请求会触发总线错误。

系统启动时,Bootloader 会读取一份预设的 MPC 配置表并写入硬件寄存器。MPC Entry 的起始和结束地址须为 4K 对齐(硬件忽略地址低 12 位),每个 MPC 最多支持 8 个 Entry。未被配置覆盖的地址区间,默认均视为 Secure(安全)属性。

配置与代码实操

要修改内存属性,开发者需要直接修改 SDK 源码中的配置表文件:{SDK}/component/soc/usrcfg/amebaxxx/ameba_boot_trustzonecfg.c,在对应的 mpc_config 数组中插入一条新的 Entry。

配置规则:

  1. 定位到目标物理内存所属的 MPC 数组(见下方对应关系表)

  2. 在数组结尾的 0xFFFFFFFF (End Flag)之前插入新条目

  3. 确保配置项指定为 MPC_NS (非安全)和 MPC_RW (可读写)

  4. 新增 Entry 后,需将 End Flag 移动到新位置

代码示例:在默认配置基础上,将 0x20010000 - 0x20011FFF 这 8K 地址空间设置为非安全可读写:

/* ameba_boot_trustzonecfg.c */
const TZ_CFG_TypeDef mpc1_config[MPC_ENTRYS_NUM] =                  /* Security configuration for sram S1/S2 */
{
//  Start                       End                 CTRL
   {0x20000000,                 0x20006FFF,         MPC_RW | MPC_NS},   /* entry0: MSP_NS, ... */
   {0x20008000,                 0x20008FFF,         MPC_RW | MPC_NS},   /* entry1: KM0_RTOS_STATIC_0_NS */
   {(u32)__km4_bd_ram_start__,  0x20100000 - 1,     MPC_RW | MPC_NS},   /* entry2: BD_RAM_NS*/

   /* 在此处插入客户自定义的 8KB 非安全共享内存区间 */
   {0x20010000,                 0x20012000 - 1,     MPC_RW | MPC_NS},   /* entry3: User Shared Buffer (NS) */
   {0xFFFFFFFF,                 0xFFFFFFFF,         MPC_RW | MPC_NS},   /* entry4: End Flag (标志配置结束) */
   {0xFFFFFFFF,                 0xFFFFFFFF,         MPC_RW | MPC_NS},   /* entry5: TODO */
   {0xFFFFFFFF,                 0xFFFFFFFF,         MPC_RW | MPC_NS},   /* entry6: TODO */
   {0xFFFFFFFF,                 0xFFFFFFFF,         MPC_RW | MPC_NS},   /* entry7: TODO */
};

每个芯片的 MPC 的 Entry 数量有所差别。各芯片 MPC 介绍如下:

RTL8721Dx:
  • mpc1_config:SRAM

  • mpc2_config:PSRAM

MPC 锁定机制:每组 MPC 均有独立的 lock 寄存器(如 IDAU1_LOCK),用于锁定配置防止运行时被篡改:

  • 当向 lock 寄存器写入 1 时,对应的 MPC 配置被锁定

  • 锁定后,该 MPC 的所有寄存器(包括 lock 寄存器本身)均无法再被修改,直至系统复位

  • 锁定后也无法通过写入 0 来解锁,唯一的解锁方式是系统复位

出于安全性考虑,默认 SDK KM4TZ Bootloader 在初始化完上述 MPC 配置后,会自动将硬件的 MPC_LOCK 寄存器置位。

PPC 配置

业务场景:独占外设控制权

与保护内存类似,客户可能需要将某个特定的 I2C 接口用于连接外部硬件安全芯片。 这种情况下,必须彻底禁止非安全主设备对该外设寄存器的任何读写操作。

底层机制与原理

PPC 负责外设总线级别的安全过滤:

  • 当外设配置为**安全(Secure)**时,硬件仅放行安全侧主设备的访问请求,非安全侧的读写会被直接硬件拦截

  • 当外设配置为**非安全(Non-Secure)**时,两侧代码均可自由访问

PPC 在上电时,Bootloader 对于部分外设自动进行默认的初始化配置。用户也可以在运行时在安全世界通过 API 修改配置项。

配置与代码实操

Bootloader 上电时会为各类外设提供一套默认的初始配置。如果开发者需要调整某个外设的归属权,可以在安全世界的初始化阶段(如 main() 函数早期),通过 SDK 提供的 API 动态修改。

/* 安全世界初始化代码 */
#include "ameba_soc.h"

void Secure_Peripheral_Protection_Init(void) {
    // 调用 TZ_ConfigSlaveSecurity 调整外设权限
    // 示例 1: 将 UART0 配置为仅安全世界可访问(ENABLE = Secure 状态)
    TZ_ConfigSlaveSecurity(UART0_DEV, ENABLE);

    // 示例 2: 将 I2C1 开放给非安全世界使用(DISABLE = Non-Secure 状态)
    // TZ_ConfigSlaveSecurity(I2C1_DEV, DISABLE);

    // 可选: 手动锁定 PPC 配置(见下方锁定机制说明)
    // HAL_WRITE32(SYSTEM_CTRL_BASE, REG_PPC_LOCK, 1);
}

各个 IC 外设列表及具体宏定义如下:

RTL8721Dx:

函数类型 TZ_ConfigSlaveSecurity(PPC_PeripheralId Perip, u32 Status)

  • Status 可以选择为 SECURE 或者 NON_SECURE

  • PPC_PeripheralId 定义在 component/soc/amebadplus/fwlib/include/ameba_trustzone.h , Perip 可选的枚举值如下表所示:

外设名称

PPC_PeripheralId

WIFI

SECFG_WIFI_CFG

BT

SECFG_BT_CFG

SECURE_ENGINE

SECFG_SECURE_ENGINE

GDMA0

SECFG_GDMA0_CFG

PPE

SECFG_PPE_CFG

SDIO

SECFG_SDIO_CFG

SPI0

SECFG_SPI0

SPI1

SECFG_SPI1

PSRAM_PHY

SECFG_PSRAM_PHY

PSRAM_SPIC_USERMODE

SECFG_PSRAM_SPIC_USERMODE

SPIC_USERMODE

SECFG_SPIC_USERMODE

QSPI

SECFG_QSPI

SPORT0_I2S

SECFG_SPORT0_I2S

SPORT1_I2S

SECFG_SPORT1_I2S

OTPC

SECFG_OTPC_CFG

SYSON

SECFG_SYSON

UART0

SECFG_UART0

UART1

SECFG_UART1

UART2_BT

SECFG_UART2_BT

UART3_LOG

SECFG_UART3_LOG

GPIOA_B

SECFG_GPIOA_B

ADC

SECFG_ADC

CAP_TOUCH

SECFG_CAP_TOUCH

KEY_SCAN

SECFG_KEY_SCAN

IPC

SECFG_IPC

DBG_TIMER

SECFG_DBG_TIMER

PMC_TIMER_0_1

SECFG_PMC_TIMER_0_1

TIMER0_7_BASIC

SECFG_TIMER0_7_BASIC

TIMER8_9_PULSE_PWM_TIMER10_11

SECFG_TIMER8_9_PULSE_PWM_TIMER10_11

TRNG_PORT1_PORT2

SECFG_TRNG_PORT1_PORT2

RXI300

SECFG_RXI300

RSIP

SECFG_RSIP

LEDC

SECFG_LEDC

PDM

SECFG_PDM

IR

SECFG_IR

I2C0

SECFG_I2C0

I2C1

SECFG_I2C1

PPC 锁定机制: 与 MPC 的开机自动锁定不同,默认 Bootloader 未使能 PPC_LOCK 锁,因此代码可以在运行时动态切换外设权限。 但在产品量产阶段,建议客户在安全初始化完成所有 TZ_ConfigSlaveSecurity 调用后,手动将 PPC_LOCK 置 1 固化配置,防止运行期间遭到恶意篡改。

  • 当向 PPC_LOCK 写入 1 时,PPC 配置被锁定

  • 锁定后,PPC 寄存器无法再被修改,直至系统复位

  • 锁定后也无法通过写入 0 来解锁,唯一的解锁方式是系统复位

特殊外设的安全控制

以下外设内部实现了更细致的安全管理机制,可对寄存器或功能进行粒度更细的访问控制:

  • 定时器(Timer)

  • 真随机数生成器(TRNG)

  • 看门狗(Watchdog)

  • 一次性可编程存储器(OTP)

  • 通用 DMA(GDMA)

  • 加密引擎(SHA/AES)

建议将上述外设的 PPC 属性配置为非安全,利用外设内部的安全控制策略进行精细化管理,以获得更高的灵活性。具体配置方法请参考对应章节的用户手册。