1.spec
RDC 配置信息被发送到结构端口、内存垫片、信号控制器和外设,以根据域分配控制访问。
结构使用与每个端口相关的域标识符,将此信息与总线事务一起包含在内。当从属加密垫圈遇到总线事务时,它会将事务域 ID 与 RDC 提供的允许域列表进行比较。如果事务域 ID 在列表中,则允许访问。
RDC 允许将外设和存储器分配到一个或多个域,而每个总线主站或内核则被置于多个域中的一个。主控器在主域分配(MDA)寄存器中分配一个域。外设在 PDAP 寄存器中获得每个域的 R/W 访问权限。内存区域由开始寄存器和结束寄存器中的地址空间(即 MRSA 和 MREA)绑定。每个内存区域在内存区域控制(MRC)寄存器中分配一个或多个允许域和 R/W 权限。内存区域必须在权限激活前启用。否则,权限将不受限制。
RDC 本身应该是隔离的,以确保只有值得信赖的资源管理人员才能配置 RDC 寄存器。例如,这一过程可以在安全启动过程中初始化,也可以在安全世界的运行过程中初始化。如果操作系统不支持运行时可信执行,则可在安全启动过程中锁定 RDC 配置,以防止在操作系统运行后进一步修改。
根据编入 RDC 的资源域分配,CCM 支持多核感知。有关核心资源域与各自 CCM 资源之间的关系,请参阅 CCM 章节。如果在更新内核资源域分配时未遵循正确的顺序,可能会导致时钟被意外门控。
Domain ID:RDC 通过使用称为 “Domain ID”(DID)的标识符来隔离域资源。内核及其资源(包括内存、总线主控和外设)都与单个 DID 相关联。当软件或 DMA 尝试访问外设或内存时,相应的总线事务包括 DID 以及其他总线控制信息,如读取、写入和特权模式。
下面这张表是MDA信息表,控制master节点的分配。
下面这张表是PDAP信息表,控制外设寄存器访问权限和原子访问功能。
支持域隔离的存储器数量因设备而异。特定内存的内存区域数量和这些区域的大小因内存垫圈而异。每个内存区域都有一组寄存器,用于根据起始地址和终止地址定义区域的边界;一个控制寄存器,用于设置域访问权限和启用区域;一个状态寄存器,用于确定是否拒绝访问某个区域。
对于该设备,请参考下表确定支持域的存储器、每个存储器的区域数、区域分辨率、存储器区域寄存器组的标识号,以及访问存储器区域寄存器组的 RDC 寄存器地址。
2.软件设置
下面是Uboot中关于RDC配置,此配置会存储到ocram指定位置,由ATF读取并写入RDC控制器,将对应模块的控制权移动到M7。配置中包含RDC_MDA(mater domian assign)
,RDC_PDAP(peripheral domian access permission)
,MRC(Memory Region Control)
。
mcu_rdc {
compatible = "imx8m,mcu_rdc";
start-config = <
RDC_MDA RDC_MDA_SDMA3p DID1 0x0 0x0
RDC_MDA RDC_MDA_ENET1_TX DID1 0x0 0x0
RDC_MDA RDC_MDA_ENET1_RX DID1 0x0 0x0
RDC_MDA RDC_MDA_SDMA3b DID1 0x0 0x0
RDC_MDA RDC_MDA_SDMA3_SPBA2 DID1 0x0 0x0
RDC_PDAP RDC_PDAP_ENET1 PDAP_D0D1_ACCESS 0x0 0x0
RDC_PDAP RDC_PDAP_SAI3 PDAP_D1_ACCESS 0x0 0x0
RDC_PDAP RDC_PDAP_UART4 PDAP_D1_ACCESS 0x0 0x0
RDC_PDAP RDC_PDAP_GPT1 PDAP_D1_ACCESS 0x0 0x0
RDC_PDAP RDC_PDAP_SDMA3 PDAP_D1_ACCESS 0x0 0x0
RDC_PDAP RDC_PDAP_I2C3 PDAP_D1_ACCESS 0x0 0x0
RDC_MEM_REGION 22 TCM_START TCM_END MEM_D1_ACCESS
RDC_MEM_REGION 39 M4_DDR_START M4_DDR_END MEM_D1_ACCESS
0x0 0x0 0x0 0x0 0x0
>;
stop-config = <
RDC_MEM_REGION 22 TCM_START TCM_END MEM_D0D1_ACCESS
RDC_MEM_REGION 39 M4_DDR_START M4_DDR_END MEM_D0D1_ACCESS
0x0 0x0 0x0 0x0 0x0
>;
};
RDC_MDA
示例,下面配置的含义为,分配ENET1_RX这个master到M核。
RDC_MDA RDC_MDA_ENET1_RX DID1 0x0 0x0
RDC_PDAP
示例,下面配置含义是设置ENET1的寄存器读写权限为,M7和A53均可读写。
#define PDAP_D0D1_ACCESS 0x0000000F /* D0R|D0W|D1W|D1R */
RDC_PDAP RDC_PDAP_ENET1 PDAP_D0D1_ACCESS 0x0 0x0
RDC_MEM_REGION
示例,M7只能访问TCM:0x7E0000-0x81FFFF
和DDR:0x80000000-0x81000000
地址范围。22和39是Memory Region Register Set Number,具体参考Table 3-4. Memory Region Mapping。
RDC_MEM_REGION 22 TCM_START TCM_END MEM_D1_ACCESS
RDC_MEM_REGION 39 M4_DDR_START M4_DDR_END MEM_D1_ACCESS
3.SDK里的设置和A核的设置有什么区别
区别:board.c中的RDC初始化只是启用域1中的时钟门控功能。
门控分类:
typedef enum _clock_gate_value
{
kCLOCK_ClockNotNeeded = 0x0U, /*!< Clock always disabled.*/
kCLOCK_ClockNeededRun = 0x1111U, /*!< Clock enabled when CPU is running.*/
kCLOCK_ClockNeededRunWait = 0x2222U, /*!< Clock enabled when CPU is running or in WAIT mode.*/
kCLOCK_ClockNeededAll = 0x3333U, /*!< Clock always enabled.*/
} clock_gate_value_t;
主函数 main
-
初始化内存和板级设置:
BOARD_InitMemory(); BOARD_RdcInit();//M7 内核运行于域 1,此时应在 CCM 中启用域 1 中以下 IP/BUS/PLL 的时钟栅极。这样可以确保 M 内核使用的外设时钟不受运行在域 0 的 A 内核的影响。 BOARD_InitBootPins(); BOARD_BootClockRUN(); BOARD_InitDebugConsole();
这些函数初始化板上的内存、RDC 设置、引脚、时钟和调试控制台。
-
初始化 GPIO:
gpio_pin_config_t pinConfig = { kGPIO_DigitalOutput, 0, kGPIO_IntRisingEdge, }; GPIO_PinInit(GPIO1, 0, &pinConfig);
配置 GPIO 引脚为数字输出模式,并初始化该引脚。
-
设置特定的寄存器值:
*(volatile uint32_t *)0x30340028 |= (0x0C);
该行代码设置寄存器
0x30340028
的特定位IOMUXC_GPR10[2:3]
,以确保内存违规触发硬错误。 -
初始化 RDC 和 SEMA42模块的时钟:
RDC_Init(APP_RDC); RDC_SEMA42_Init(APP_RDC_SEMA42);
-
分配当前主域 ID:
RDC_GetDefaultMasterDomainAssignment(&assignment); assignment.domainId = APP_CUR_MASTER_DID; RDC_SetMasterDomainAssignment(APP_RDC, APP_CUR_MASTER, &assignment);
-
调用 RDC 演示函数:
APP_RDC_Periph(); APP_RDC_PeriphWithSema42(); APP_RDC_Mem();
依次调用三个函数,分别演示 RDC 在不同场景下的使用:
APP_RDC_Periph
: 演示外设访问控制。APP_RDC_PeriphWithSema42
: 演示外设访问控制和 SEMA42 的结合使用。APP_RDC_Mem
: 演示内存区域访问控制。
RDC 演示函数
-
APP_RDC_Periph
:- 配置外设访问权限,使其可被所有域访问。
- 访问外设并检查是否发生故障。
- 配置外设访问权限,使其不可被当前域访问。
- 再次访问外设并检查是否发生故障。
static void APP_RDC_Periph(void) { PRINTF("RDC Peripheral access control\r\n"); s_demoState = kRDC_DEMO_Periph; /* * Item 1: Peripheral accessible. */ RDC_GetDefaultPeriphAccessConfig(&periphConfig); periphConfig.periph = APP_RDC_PERIPH; /* Set peripheral to accessible by all domains. */ RDC_SetPeriphAccessConfig(APP_RDC, &periphConfig); s_faultFlag = false; APP_TouchPeriph(); /* Peripheral is accessible, there should not be hardfault. */ DEMO_CHECK(false == s_faultFlag); /* * Item 2: Peripheral inaccessible. */ /* Make peripheral not accessible. */ periphConfig.policy &= ~(RDC_ACCESS_POLICY(APP_CUR_MASTER_DID, kRDC_ReadWrite)); RDC_SetPeriphAccessConfig(APP_RDC, &periphConfig); s_faultFlag = false; APP_TouchPeriph(); /* Peripheral is not accessible, there should be hardfault. */ DEMO_CHECK(true == s_faultFlag); }
-
APP_RDC_PeriphWithSema42
:- 配置外设访问权限,并启用 SEMA42。
- 确保当前核心未持有 SEMA42 门。
- 尝试访问外设,预期会发生故障。
- 恢复外设访问默认策略。
static void APP_RDC_PeriphWithSema42(void) { PRINTF("RDC Peripheral access control with SEMA42\r\n"); /* Demo the SEMA42 used together with RDC. */ s_demoState = kRDC_DEMO_PeriphSema42; RDC_GetDefaultPeriphAccessConfig(&periphConfig); periphConfig.periph = APP_RDC_PERIPH; periphConfig.enableSema = true; RDC_SetPeriphAccessConfig(APP_RDC, &periphConfig); /* Make sure current core does not hold the SEMA42 gate. */ RDC_SEMA42_Unlock(APP_RDC_SEMA42, APP_RDC_SEMA42_GATE); DEMO_CHECK(APP_CUR_MASTER_DID != RDC_SEMA42_GetLockDomainID(APP_RDC_SEMA42, APP_RDC_SEMA42_GATE)); s_faultFlag = false; APP_TouchPeriph(); /* Peripheral is not accessible because SEMA42 gate not locked, there should be hardfault. */ DEMO_CHECK(true == s_faultFlag); /* Demo finished, make the peripheral to default policy. */ RDC_GetDefaultPeriphAccessConfig(&periphConfig); /* Set peripheral to accessible by all domains. */ RDC_SetPeriphAccessConfig(APP_RDC, &periphConfig); RDC_SEMA42_Unlock(APP_RDC_SEMA42, APP_RDC_SEMA42_GATE); }
-
APP_RDC_Mem
:- 配置内存区域访问权限,使其不可被当前域访问。
- 尝试访问内存,预期会发生故障。
- 检查内存访问故障状态,恢复内存区域访问权限。
static void APP_RDC_Mem(void) { /* * In memory protection, please notice the cache's effect. * For example, if a memory region has been loaded to cache * before it is set not accessible, then CPU only access the * cache but not the memory, application could not detect * access violation. */ PRINTF("RDC memory region access control\r\n"); s_demoState = kRDC_DEMO_Mem; RDC_GetDefaultMemAccessConfig(&memConfig); memConfig.mem = APP_RDC_MEM; memConfig.baseAddress = APP_RDC_MEM_BASE_ADDR; memConfig.endAddress = APP_RDC_MEM_END_ADDR; /* Make memory not accessible. */ memConfig.policy &= ~(RDC_ACCESS_POLICY(APP_CUR_MASTER_DID, kRDC_ReadWrite)); RDC_SetMemAccessConfig(APP_RDC, &memConfig); #if APP_USING_CACHE /* * Invalidate the cache, so new read will read from memory directly, * to make sure trigger read error. */ DCACHE_InvalidateByRange(APP_RDC_MEM_BASE_ADDR, APP_RDC_MEM_END_ADDR - APP_RDC_MEM_BASE_ADDR); #endif s_faultFlag = false; APP_TouchMem(); #if APP_USING_CACHE /* * Flush the cache, so the modified data is written to memory, * to make sure trigger write error. */ DCACHE_CleanInvalidateByRange(APP_RDC_MEM_BASE_ADDR, APP_RDC_MEM_END_ADDR - APP_RDC_MEM_BASE_ADDR); __DSB(); #endif /* Memory is not accessible, there should be hardfault. */ DEMO_CHECK(true == s_faultFlag); DEMO_CHECK(0 == memDemoError); }