目录
  • CPU是如何支持安全中断的?
  • GIC是如何支持安全中断的?
  • 外设如何支持安全中断
  • TF-A2.6.0 源码分析

前几篇博文主要梳理了GIC V3架构基础,中断全生命周期,以及中断IRQ、FIQ基础概念。那在ARM TrustZone是如何支持安全中断的呢?

ARM TrustZone一个非常大的优势就是很容易管理外设,管理外设包含了:外设访问权限,外设作为Master时怎么发出安全访问或非安全访问,以及外设如何产生安全中断,以及CPU如何响应这个中断等。我们今天主要介绍如何产生安全中断,由三个部分来参与:安全外设,GIC,以及CPU。

CPU是如何支持安全中断的?

对于CPU本身来说对中断是安全还是非安全是无感知的,CPU只区分到底是IRQ,还是FIQ。当CPU收到中断时会根据状态寄存器来检查这个中断是否被屏蔽了,例如PSTATE.DAIF,其中I bit标识IRQ是否被Mask,F bit标识FIQ是否被Mask。如果没有被Mask的话,CPU可能会处理这个中断。

那这个中断到底在哪里处理呢?例如Armv8-A CPU分为EL0,EL1,EL2,EL3,EL0是不处理中断的,EL1,EL2,EL3都可以处理中断,具体在哪些EL处理中断,是有系统寄存器来决定的,例如SCR_EL3.FIQ和SCR_EL3.IRQ来标志来了对应的中断后,CPU是否直接进入到EL3所对应的exception handler。但是exception handler具体怎么处理这个中断,那是软件的事情,例如直接处理中断,还是作为一个event来做其他的事情,都是软件来控制的。 可见对于CPU来说是不知道中断是安全还是非安全,只是根据收到的中断类型来做两件事:屏蔽了吗?如果没屏蔽,在哪里处理?目前在Armv7-A的架构中,FIQ当安全中断,在Armv8-A和GICv3架构中FIQ不表示安全中断,表示来了中断,当前EL可能处理不了,需要换个地方处理

GIC是如何支持安全中断的?

GIC也在不断向前演进,现在最新的应该GICv4,从GICv1就开始支持TrustZone,GICv2相对GICv1来说主要是支持虚拟化,GICv3相对GICv2改动较大,支持Message base interrupt,支持的CPU更多,在安全方面也做了一些增强,GICv4在虚拟化上做了增强。

GICv1和GICv2主要是配合Armv7-A的CPU,是怎么支持TrustZone的呢?结合Armv7-A的架构,因为想把FIQ预留给安全中断,那么GIC主要由两点来实现,第一个是把安全分Group,Group 0给安全中断可以产生IRQ或者FIQ,Group 1给非安全中断只能产生IRQ。第二个把跟FIQ相关的registers都需要安全来配置,例如Group 0相关的配置,只有CPU在安全状态进行配置,这样做来达到安全的目的。

GICv3主要是配合Armv8-A的CPU,但是Armv8-A相对Armv7-A的TrustZone进行了优化,Secure World分为S-EL1(Trusted OS)和EL3(Secure Monitor)是不同的特权级,如果再像GICv2那样分为Group 0 和Group 1,Group 0如果同时给S-EL1和EL3使用,其实是不够的。所以在GICv3时分成了三个Group,Group0,Secure Group 1和non-secure Group 1,这样就很容易区分,例如Group 0 给EL3使用,Secure Group 1给S-EL1用,Non-secure Group 1给EL1来用,这样划分就很合理,但是也存在另外一个问题,如果三个Group,而CPU侧只有两个信号线IRQ和FIQ,怎么区分这三个Group呢?这样也是很多开发者容易误解的地方,在GICv3的GIC CPU interface是跟CPU做在一起的,也是说GIC Distributor把中断发送到GIC CPU interface后,CPU interface是知道CPU在哪个EL,以及安全状态还是非安全状态,CPU根据中断所在的Group,以及CPU的状态来决定到底是发IRQ,还是FIQ,这样用两个中断信号很好解决区分三个Group的问题。

不过这里要注意的FIQ不在表示安全中断,例如对于Non-secure Group 1的中断,如果CPU在Secure world,CPU interface就给CPU发送一个FIQ,如果CPU在Non-secure world就发一个IRQ。CPU收到这个IRQ或者IFQ,是否被屏蔽,以及在哪处理,是由上文提到的CPU系统寄存器来决定的,跟GIC没关系。

外设如何支持安全中断

在GICv3中,中断分为SGI,PPI,SPI,LPI。其中LPI只有Non-secure Group 1,也就是说只能产生非安全中断,其他三个可以配置成Group 0,Non-secure Group 1或Secure Group 1。SGI是software generated interrupts,一般是做核间通讯来用的,PPI是private peripheral interrupt主要是给CPU的私有外设来用的,一般是安全外设是做成SPI,shared peripheral interrupt。

TF-A2.6.0 源码分析
void __init gicv3_distif_init(void)
{
	unsigned int bitmap;

	assert(gicv3_driver_data != NULL);
	assert(gicv3_driver_data->gicd_base != 0U);

	assert(IS_IN_EL3());

	
	gicd_clr_ctlr(gicv3_driver_data->gicd_base,
		      CTLR_ENABLE_G0_BIT |
		      CTLR_ENABLE_G1S_BIT |
		      CTLR_ENABLE_G1NS_BIT,
		      RWP_TRUE); // 当前中断被disabled,清除分组信息,当前函数做重新配置

	
	gicd_set_ctlr(gicv3_driver_data->gicd_base,
			CTLR_ARE_S_BIT | CTLR_ARE_NS_BIT, RWP_TRUE); // 亲和路由是能

	
	
	gicv3_spis_config_defaults(gicv3_driver_data->gicd_base);  
 
	bitmap = gicv3_secure_spis_config_props(
			gicv3_driver_data->gicd_base,
			gicv3_driver_data->interrupt_props,
			gicv3_driver_data->interrupt_props_num);

	
	gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE); //使能上述配置的SPIs
}

unsigned int gicv3_secure_spis_config_props(uintptr_t gicd_base,
		const interrupt_prop_t *interrupt_props,
		unsigned int interrupt_props_num)
{
	unsigned int i;
	const interrupt_prop_t *current_prop;
	unsigned long long gic_affinity_val;
	unsigned int ctlr_enable = 0U;

	
	if (interrupt_props_num > 0U) {
		assert(interrupt_props != NULL);
	}

	for (i = 0U; i < interrupt_props_num; i++) {
		current_prop = &interrupt_props[i];

		unsigned int intr_num = current_prop->intr_num;

		
		if (!IS_SPI(intr_num)) {
			continue;
		}

		
		gicd_clr_igroupr(gicd_base, intr_num);

		
		assert((current_prop->intr_grp == INTR_GROUP0) ||
				(current_prop->intr_grp == INTR_GROUP1S));

		if (current_prop->intr_grp == INTR_GROUP1S) {
			gicd_set_igrpmodr(gicd_base, intr_num);
			ctlr_enable |= CTLR_ENABLE_G1S_BIT;
		} else {
			gicd_clr_igrpmodr(gicd_base, intr_num);
			ctlr_enable |= CTLR_ENABLE_G0_BIT;
		}

		
		gicd_set_icfgr(gicd_base, intr_num, current_prop->intr_cfg);

		
		gicd_set_ipriorityr(gicd_base, intr_num,
					current_prop->intr_pri);

		
		gic_affinity_val =
			gicd_irouter_val_from_mpidr(read_mpidr(), 0U);
		gicd_write_irouter(gicd_base, intr_num,
					gic_affinity_val);

		
		gicd_set_isenabler(gicd_base, intr_num);
	}

	return ctlr_enable;
}

1.《ARM GIC ARM TrustZone如何支持安全中断 分析笔记。》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《ARM GIC ARM TrustZone如何支持安全中断 分析笔记。》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/jiaoyu/2372144.html