GIC_V3 初始化设置
在GIC 介绍之一和二中,可以看到GIC的逻辑结构和硬件框架。
那么在初始化时候,需要将各个组件配置起来,Interrupt Controller中的Distributor,Redistributor,CPU Interface需要按照不同顺序配置。既然Distributor是所有CPU都可以对应的,那么这个只要在第一个CPU启动时配置就可以了,对于Redistributor和CPU Interface是PER-CPU对应的,所以,在SECONDARY CPU启动时候需要配置各自的组件。这也是下图中所表示的:
我们从Distributor初始化开始。
GIC的初始化主要是通过配置其对应的寄存器来实现。我们介绍过
Distributor上的主要寄存器,实际上,在实现中,也就是通过配置这些Registers来实现的。
我们参考的代码基于X20,并且首先在Ex3的ATF安全世界里面先启动,这个并不妨碍对流程的认识:
一个ARM设备在启动时候,首先上电,之后会从片内的BOOT ROM获取到启动初始代码,在这里做基本的芯片初始化,而后去
加载PRE-LOADER部分,这部分在X20上面也是做如MEMORY,UART等的设置。如果说没有加载ATF(ARM TRUSTZONE FIRMWARE)的话,就会继续LK和KERNEL的流程,在X20上面因为要做TRUSTZONE的需要,所以在这里加载了ATF,同时,GIC的基本设置和最初的INIT也是在这里。
这里的代码结构主要:
从Gic_setup入口,在这里,首先获取到 gicd的基地址,之后才可以在这个基础上向不同寄存器写入配置数据。
如上图,之后要最的是通过控制CTLR寄存器先把Distributor功能关闭,并配置所有的SPI发送目标域为NS-G1,在这里就是KERNEL。并重新开启Distributor。
之后会去进入gic_cpuif_init,对相应的Redistributor和CPU Interface做设置。
同样道理,先获取到Redistributor的基地址,之后用于配置。首先要设置WAKEUP,这样保证CPU Interface和对应的CPU在之后能够被唤醒。
在Distributor配置了SPI的目标域,但是没有配置SGI和PPI,对于这32个IRQ,这里在Redistributor进行配置,也是送到NS-G1,也就是KERNEL里面处理。
ICC_SRE的开启,是要激活CPU Interface上对应的寄存器,激活后,向对应的寄存器里面写入信息,才会有相应的动作,否则是不起作用的。
enable_sgi_fiq,这里补充说明下,本身是可选的,X20的平台上面把SGI,将13(FIQ_SMP_CALL_SGI)号SGI 作为优先级最高的一个IRQ配置,在KERNEL 有HANDLE_FIQ_AS_NMI , 目的也是想DUMP 出关键信息,目前代码只有部分框架。如果可以,后面看看其它厂家是不是也是这样考虑的。
这里在Ex3里面就做了基本的配置了,那么之后其它组件的设置好,进入KERNEL之后,还有其它部分要补充,这样,中断才能通畅到达KERNEL里面并处理。
上图里面体现的是第一个CPU起来之后,进入KERNEL里面,开始逐步进行INIT,调用init_IRQ,这是现在KERNEL的框架结构,一直到irqchip_init,of_irq_init,在of_irq_init里面不同的架构厂商或者说芯片的定制者,就可以增加特定的GIC代码了,比如说X86__IRQ_INIT,当然这只是设定个例子。在X20上,使用的是GIC_V3对应的GIC设备,这里增加的是gic_of_init来完成特定的初始化。
那么在这里面做了什么呢?
其实也比较简单:
1. irq_domain and initialize it. 我们从GIC侧获得IRQ是硬件类型的中断号,而我们通常意义上在KERNEL里面处理的是软件层面的IRQ,比如用desc_irq表示的中,这样两者之间就需要映射,座椅就用到了irq_domain,原理如此,我们在后面会展开来说如何实现的。
2. 给系统的中断入口设置特定体系的函数,一般是handle_irq,这里是gic_handle_irq。
3. 设定IPI消息的句柄,当发送IPI消息时候就可以调用到gic_raise_softirq,这个在前面介绍GIC寄存器时候,在GICC_SGI部分说明到。
另外要给出gic_cpu_notifier作为CPU ON时候的通知链条上的一环,在这里面会增加SECONDARY CPU的设置。
4. gic_dist_init,这只开启G1的ARE功能,并将所有的SPI发送目标定位全体CPU,这由使用者自己来定义,可以将特定的IRQ直接绑定到特定的CPU上处理。
5. gic_cpu_init对CPU对应的INTERFACE进行设置。我们可以看下里面的详细内容:
gic_enable_redist , 设置WAKE PROCESSORSLEEP bit.
Read WAKEUP_ChildrenAsleep ,确保标识已经变化。这里实际上CPU 已经起来了,读取这个标识为了明确该位置,不会对后续操作产生困扰(理解如此)。
gic_enable_sre 这里只是对ICC 也就是CPU interface 的Enable system registers , 并没有对EL2, EL3操作, 而不像CORE_0那样。在V3版的ATF代码里面,有提供操作的接口,并且
是PER_CPU的,是不是后面也提供了其它CPU进入EL3操作对应的EL2, EL3对应的GIC寄存器,应该是这样。
DEFAULT_PMR_VALUE 0xf0, GICD 里面设置为0XA0, 所以X20里面中断是高于这个门限优先级。
ICC_IGRPEN1
Controls whether Group 1 interrupts are enabled for the current Security state 。这个开启后,就可以接收到G1-NS的中断了。
至此,当第一个CPU起来之后,对应的GIC设置完成,可以接受驱动的终端配置,并且接受终端处理了。
那么,当系统中有多个CPU,比如X20上有10个CPU(CORE),其它9个如何设置呢。
上图中,左侧部分蓝色框,表示CPU_0在KERNEL对应的流程,这里简化。在上面介绍过:gic_cpu_notifier.notifier_call= gic_secondary_init,也就是主要注册了gic_secondary_init。
在右侧绿色部分表示CPU_0~9在KERNEL里面的流程,这里只列出和GIC关联部分。入口是Secondary_start_kernel,那么之后会Notify_cpu_starting,在这里调用到Cpu_notify。其中Notify_cpu_chain就用到了之前注册的gic_secondary_init,图中下册也给出了代码的入口,这样CPU_0~9也不需要再初始化DISTRIBUTOR,只需要执行gic_cpu_init,设置自己对应的部分就可以了。
OK,到这里,硬件上的配置流程介绍完,下面整理并介绍如何把HARDWARE IRQ和软件处理上的IRQ desc_irq联系起来……