powerpc架构 machine check会使core进入什么状态

来自:&&&《》
更多精彩,关注微信号:360doc
馆友评论(0)
您好,请&&或者&&后再进行评论
合作登录:
(window.slotbydup = window.slotbydup || []).push({
container: s,
id: 'u2363571',
scale: '20.3',
display: 'inlay-fix'powerpc-linux内核start_kernel之前启动分析-开门见山 - 其他综合 - 红黑联盟
powerpc-linux内核start_kernel之前启动分析-开门见山
前段时间做了公司内核从arm处理器到一款powerpc处理器的移植(公司处理器换核,前期用FPGA仿真板进行了芯片验证和软件移植),借这个机会也学习了powerpc处理器的一些知识,对powerpc的内核启动也有所了解。
完成了arm版本内核start_kernel之前汇编分析,也想尝试写几篇文章对powerpc的启动进行分析,与大家分享。
在分析之前,首先罗嗦几句,这次移植工作之前我从来没有接触过powerpc相关的知识,因此是作为一个初学者来完成这次移植工作的。
初学者要完成一次完整的内核移植,我觉得首要一点是认真学习该处理器核的芯片手册,以我移植的处理器(ppc460s)为例,首先要找到该处理器核的芯片手册,不像SOC芯片手册会介绍SOC的外设资源,处理器核芯片手册介绍的是处理器内部逻辑以及特性,手册需要重点关注的几点有:
(1)地址空间管理,包括上电取指地址,MMU如何配置使用等
(2)寄存器定义,包括通用寄存器,特殊功能寄存器
(3)中断异常的定义,异常向量表如何定义和使用
(4)cache管理
(5)处理器核reset后如何初始化,处理器核手册会给出一些标准的步骤
(6)处理器核特定资源介绍,如ppc460s核内集成了timer,有指定指令进行操作
以上所述都是处理器核的特性,肯定会在处理器核的手册中进行介绍,学习了这些,再对基于该处理器核的SOC地址空间布局(看SOC手册,地址空间分布是由处理器核外的地址总线仲裁决定的)加以学习,对这款处理器就有了内在的了解。
当然处理器指令集也是一项处理器核特性,各架构处理器表面看最大的区别就是指令集不一样,但是我个人感觉指令集并不需要特别的关注,只需要看懂指令含义,会写最基本的指令即可,指令是我们操纵处理器的工具。处理器的核心区别还是在上述的几点。
每种架构处理器都会有些通用的处理器特性定义,也有专门的资料来介绍,可以与处理器核手册配合着看,如powerpc的BOOKE,MIPS的《see mips run》,ARM的《ARM指令体系架构》等。这些资料都对处理器的上述特性做了一些通用的定义。
linux内核在start_kernel之前的大部分工作都是对处理器核的一个初始化工作,将整个内核启动过程全部了解后你就会发现,内核启动其实就是一个处理器SOC由内到外进行初始化的过程!
了解了处理器核的特性,接下来就可以进行内核移植,内核移植的大体思路我总结了一篇文章,连接如下:
http://blog.csdn.net/skyflying2012/article/details/
linux内核的入口是在_start,对于如何根据链接脚本来判断内核入口函数可以看我的arm内核启动分析第一篇文章:
http://blog.csdn.net/skyflying2012/article/details/
kernel版本:3.4.55
处理器:ppc460s
今天首先来分析_start的前几行汇编,ppc460s属于ppc的44x系列,根据arch/powerpc/kernel/Makefile,_start在arch/powerpc/kernel/head_44x.S中。powerpc各个系列处理器差别较大,因此head文件也有好多个,本文只分析460s所属的head_44x.S,首先来看前几行汇编:
_ENTRY(_stext);
_ENTRY(_start);
* nop指令在后面会被abatron_pteptrs页表指针替换掉
/* save device tree ptr */
/* CPU number */
//没有选RELOCATABLE,不走该分支
#ifdef CONFIG_RELOCATABLE
* Relocate ourselves to the current runtime address.
* This is called only by the Boot CPU.
* relocate is called with our current runtime virutal
* address.
* r21 will be loaded with the physical runtime address of _stext
/* Get our runtime address */
/* Make it accessible */
r21,r21,(_stext - 0b)@ha
r21,r21,(_stext - 0b)@l
/* Get our current runtime base */
* We have the runtime (virutal) address of our base.
* We calculate our shift of offset from a 256M page.
* We could map the 256M page we belong to at PAGE_OFFSET and
* get going from there.
lis r4,KERNELBASE@h
ori r4,r4,KERNELBASE@l
r6,r21,0,4,31
/* r6 = PHYS_START % 256M */
r5,r4,0,4,31
/* r5 = KERNELBASE % 256M */
/* r3 = r6 - r5 */
add r3,r4,r3
/* Required Virutal Address */
init_cpu_state
内核与bootloader有传参约定,主流bootloader都会做适配,对于powerpc来说,传参约定如r3中存储设备树地址,r4 r5存储ramdisk首尾地址,r6 r7存储命令行首尾地址。
将设备树地址存储在r31之后,_start跳转到init_cpu_state执行,这也是本文重点分析的函数,如下:(将不走的条件编译以及部分注释去掉)
_GLOBAL(init_cpu_state)
//返回地址保存在r22寄存器中
* In case the firmware didn't do it, we apply some workarounds
* that are good for all 440 core variants here
r3,SPRN_CCR0
//CCR0-1寄存器是ppc的核心控制寄存器,配置处理器核的一些特性,如cache
r3,r3,0,0,27
/* disable icache prefetch */
SPRN_CCR0,r3
* 对处理器64个TLB进行搜索,找到当前代码所在TLB。
* 将其余TLB全部无效掉
//因当前代码全部是指令存储而不是数据存储,因此保证MSR的IS置1
//再进行tlbsx,查找当前代码所使用TLB编号
/* Load our current PID-&MMUCR TID and MSR IS-&MMUCR STS */
r3,SPRN_PID
/* Get PID */
/* Get MSR */
r4,r4,MSR_IS@l
/* TS=1? */
beq wmmucr
/* If not, leave STS=0 */
r3,r3,PPC44x_MMUCR_STS@h
/* Set STS=1 */
wmmucr: mtspr
SPRN_MMUCR,r3
/* Put MMUCR */
//找代码所使用的TLB,找到后将剩余63个TLB无效掉
/* Find our address */
invstr: mflr
/* Make it accessible */
/* Find entry we are in */
/* Start at TLB entry 0 */
/* Set PAGEID inval value */
/* Is this our entry? */
beq skpinv
/* If so, skip the inval */
r3,r4,PPC44x_TLB_PAGEID
/* If not, inval the entry */
skpinv: addi
/* Increment */
/* Are we done? */
/* If not, repeat */
/* If so, context change */
* 在63号TLB配置固定的内核起始地址静态映射,63号TLB以后就不会再修改了,之后用户空间动态映射时只是用0-62号TLB
* 以我的460s处理器为例,这里就需要进行修改移植,内存物理地址起始是0x.需要将下面的0改为0x8000000
* ARM获取起始物理地址的方法是根据当前的PC值,这种方法兼容性好,移植不需要修改head.c文件。
* ppc可以借鉴这种方法,不过需要首先根据当前使用的TLB来获取物理地址才可以。这是我提出的一点改进意见。
* 这里完成的是0x==&0xc6MB静态映射
lis r3,PAGE_OFFSET@h
ori r3,r3,PAGE_OFFSET@l
/* Kernel is at the base of RAM */
/* Load the kernel physical address */
/* Load the kernel PID = 0 */
SPRN_PID,r0
/* Initialize MMUCR */
SPRN_MMUCR,r5
/* pageid fields */
/* Mask off the effective page number */
ori r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_256M
/* xlat fields */
/* Mask off the real page number */
/* ERPN is 0 for first 4GB page */
/* attrib fields */
/* Added guarded bit to protect against speculative loads/stores */
ori r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G)
/* TLB slot 63 */
r3,r0,PPC44x_TLB_PAGEID /* Load the pageid fields */
r4,r0,PPC44x_TLB_XLAT
/* Load the translation fields */
r5,r0,PPC44x_TLB_ATTRIB /* Load the attrib/access fields */
//这里之前,内核的运行地址与链接地址(0xc开头)不一定一致(这要看bootloader中如何配置TLB),
//好在都是一些汇编,没有绝对寻址,保证了位置无关。
//63号TLB填写完毕后,该TLB映射的虚拟地址保证了与内核链接地址一致,接下来就需要跳转到与链接地址一致的地址运行。
//这里采用的是标号跳转,因为汇编中的标号地址是在链接时确定下来的,标号地址是链接地址。
//rfi指令会跳转到SRR0中的地址执行,就完成了地址的跳转!
/* Force context change */
SPRN_SRR1, r0
lis r0,3f@h
ori r0,r0,3f@l
SPRN_SRR0,r0
//跳转到链接地址后,将原来的TLB无效掉
/* If necessary, invalidate original entry we used */
r6,r23,PPC44x_TLB_PAGEID
//除了内核起始内存地址的256MB静态映射,如果需要早期调试串口
//则可将串口IO所在地址空间在62号TLB进行静态映射
//对于移植工作,串口打印还是非常重要的调试工具
#ifdef CONFIG_PPC_EARLY_DEBUG_44x
/* Add UART mapping for early debug. */
/* pageid fields */
lis r3,PPC44x_EARLY_DEBUG_VIRTADDR@h
ori r3,r3,PPC44x_TLB_VALID|PPC44x_TLB_TS|PPC44x_TLB_64K
/* xlat fields */
lis r4,CONFIG_PPC_EARLY_DEBUG_44x_PHYSLOW@h
ori r4,r4,CONFIG_PPC_EARLY_DEBUG_44x_PHYSHIGH
/* attrib fields */
r5,(PPC44x_TLB_SW|PPC44x_TLB_SR|PPC44x_TLB_I|PPC44x_TLB_G)
/* TLB slot 0 */
r3,r0,PPC44x_TLB_PAGEID
r4,r0,PPC44x_TLB_XLAT
r5,r0,PPC44x_TLB_ATTRIB
/* Force context change */
#endif /* CONFIG_PPC_EARLY_DEBUG_44x */
//将16种异常的入口偏移量写入IVOR0-15寄存器
/* Establish the interrupt vector offsets */
SET_IVOR(0,
CriticalInput);
SET_IVOR(1,
MachineCheck);
SET_IVOR(2,
DataStorage);
SET_IVOR(3,
InstructionStorage);
SET_IVOR(4,
ExternalInput);
SET_IVOR(5,
Alignment);
SET_IVOR(6,
SET_IVOR(7,
FloatingPointUnavailable);
SET_IVOR(8,
SystemCall);
SET_IVOR(9,
AuxillaryProcessorUnavailable);
SET_IVOR(10, Decrementer);
SET_IVOR(11, FixedIntervalTimer);
SET_IVOR(12, WatchdogTimer);
SET_IVOR(13, DataTLBError44x);
SET_IVOR(14, InstructionTLBError44x);
SET_IVOR(15, DebugCrit);
head_start_common
init_cpu_state完成的主要工作有2个:TLB初始化和异常初始化。
TLB初始化有一个很有意思也非常重要的前提是powerpc的44x系列处理器核MMU是常开的,上电即开启。
powerpc上电第一条指令是在0xfffffffc,其地址空间最上部的4k空间在上电时是被一个影子TLB映射到物理地址。
该物理地址在何处则由处理器核外地址总线仲裁决定了,我的处理器逻辑是映射在了片内SRAM中。44x系列处理器有64个TLB,影子TLB不属于其中,它对软件来说是透明的,而64个TLB软件可以通过tlbwe tlbsx等指令进行操作,以上这些知识都可以在处理器核手册中看到。
因为460s的MMU常开,因此一上电bootloader就是运行在虚拟地址上。
powerpc的bootloader启动代码一般的设计为0xfffffffc处为跳转指令,跳转到4k空间起始的0xfffff000执行,对64个TLB进行配置,之后就可以访问我们想要访问的地址空间了。
bootloader中会对整个地址空间进行TLB配置,bootloader不需要太大的运行空间,一般都是配置为平映射。
所以与ARM内核不同,powerpc内核在启动时已经是运行在虚拟地址上了。但是否与其链接地址一致,内核是不能保证的。
所以出现了init_cpu_state中前部代码的情况,首先利用tlbsx找到当前代码所使用的TLB,无效掉剩余63个TLB,之后将内存起始物理地址静态映射到内核中的内存起始虚拟地址PAGE_OFFSET,该地址也是内核的链接地址。最后跳转到链接地址运行,之后就不用担心运行地址与链接地址不一致的情况了。内核进入了全新舒适的运行环境!
其实不管哪个平台处理器,内核启动之初,因为运行地址无法保证与链接地址一致,都要完成一次地址的配置和跳转来保证内核运行地址与链接地址的一致性。
ARM内核的做法首先建立开启MMU函数turn_mmu_on所在页的一个平映射,同时创建内核起始地址的线性映射,然后开启MMU,完成turn_mmu_on函数物理运行地址到虚拟运行地址的一个无缝衔接,之后在进行链接地址的跳转。具体的做法可以参考ARM启动分析的第二篇文章,链接如下:
http://blog.csdn.net/skyflying2012/article/details/
这样对比来看,powerpc相比于ARM则更加开门见山,一开始就完成了MMU配置和跳转,进入了舒适的运行环境。这里可以看出,powerpc与ARM在启动之初地址跳转方式不同,究其原因就是powerpc的MMU是常开的!
init_cpu_state的另一个主要工作是异常初始化,异常是处理器异步处理事务的重要手段,处理器设计中都会有异常向量表来表征处理器所有可处理器的异常入口地址。产生该异常,处理器核逻辑就会跳转到对应入口地址取指运行。
对于powerpc处理器中设计2种寄存器,IVPR寄存器来指定异常向量表的入口地址,IVOR0-15寄存器则指定了powerpc处理器16种异常对应入口地址的偏移量。IVOR0-15和16种异常的对应关系如下图,此图截于460s处理器核手册。
我们最常用的是external interrupt,该异常是与外部中断控制器相连。
在init_cpu_state最后阶段调用SET_IVOR分别设置了IVOR0-15的值,该值是SET_IVOR的第二个参数的低16位,SET_IVOR实现如下。
#define SET_IVOR(vector_number, vector_label)
r26,vector_label@l;
SPRN_IVOR##vector_number,r26;
那init_cpu_state中SET_IVOR的第二个参数如CriticalInput是哪里定义的呢,接着往下看。
init_cpu_state最后调用head_start_common,如下:
head_start_common:
/* Establish the interrupt vector base */
lis r4,interrupt_base@h /* IVPR only uses the high 16-bits */
SPRN_IVPR,r4
* If the kernel was loaded at a non-zero 256 MB page, we need to
* mask off the most significant 4 bits to get the relative address
* from the start of physical memory
r22,r22,0,4,31
r22,r22,PAGE_OFFSET@h
将interrupt_base的高16位写入IVPR,因此interrupt_base代表了异常向量表的入口地址。interrupt_base定义如下:
interrupt_base:
/* Critical Input Interrupt */
CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception)
/* Machine Check Interrupt */
CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
MCHECK_EXCEPTION(0x0210, MachineCheckA, machine_check_exception)
/* Data Storage Interrupt */
DATA_STORAGE_EXCEPTION
/* Instruction Storage Interrupt */
INSTRUCTION_STORAGE_EXCEPTION
/* External Input Interrupt */
EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE)
都是一些宏定义,来看其中一个EXCEPTION的定义:
* Exception vectors.
#define EXCEPTION(n, label, hdlr, xfer)
EXCEPTION_PROLOG;
r3,r1,STACK_FRAME_OVERHEAD;
xfer(n, hdlr)
#define EXC_XFER_TEMPLATE(n, hdlr, trap, copyee, tfer, ret)
stw r10,_TRAP(r11);
r10,MSR_KERNEL;
copyee(r10, r9);
这些宏定义这里就不详细说了,定义了32字节对齐的异常处理label,很明显可以看出来EXCEPTION的第二个参数作为了宏定义实现中的label。所以interrupt_base中的宏定义就是定义了以SET_IVOR第二个参数为名的异常处理label(也可称为函数)。
interrupt_base以及EXCEPTION等宏定义定义的CriticalInput等一系列异常处理都是label,label的地址是在链接是确定的。现在内核已经运行在与链接地址一致的地址上了
因此在head_start_common中将interrupt_base的高16位写入IVPR,将CriticalInput等label的低16位写入IVOR0-15,就完成了异常向量表的初始化。
init_cpu_state对TLB重新配置,保证了链接地址与运行地址的一致,但当前的TLB配置与刚进入init_cpu_state是不一致的。因此r22保存的_start中bl init_cpu_state的下条指令地址已经变了,当前的_start已经运行在了新地址上。
所以head_start_common最后对r22中返回地址进行了处理,将其高位修改为链接地址再执行blr,保证可以正确跳回新的地址,内核跳转又重新回到了_start中!IBM Bluemix
点击按钮,开始云上的开发!
developerWorks 社区
了解处理器的中断处理逻辑和中断控制器的逻辑与编程接口,是深入理解操作系统中断子系统的基础。本文结合 PowerPC Linux 的相关实现细节,介绍 PowerPC 处理器的中断处理和 MPIC。
, 研究员, 诺基亚西门子网络
洪志国,硕士毕业于武汉大学计算机学院,现居北京。诺西 HW 和 Computing 部门研究员,研究方向为多核计算和 LTE MAC 协议栈实现。
PowerPC 异常类型与异常向量将处理器内部产生的异常和外部中断信号统一称为异常 (Exception)。异常发生时,PowerPC 处理器根据异常的类型,分别跳转到不同的异常向量地址,执行异常处理程序。异常类型到异常向量的映射,经典 PowerPC 和 Book E 有所不同。经典 PowerPC异常类型异常向量偏移 (0X)说明
System Reset
Machine Check
Data Storage Exception
Instruction Storage Exception
External Interrupt00500
所有外部中断信号
本文只关注外部中断,完整内容请看处理器手册
根据异常类型得到偏移 offset, 异常向量的物理地址为 :MSR[IP]=0 时,Vector =MSR[IP]=1 时,Vector = offset | 0xFFF00000;其中 MSR[IP] 代表 Machine State Register 的 Interrupt Prefix 比特,该比特用来选择中断向量的地址前缀。Book E异常类型异常向量偏移寄存器说明
Critical Input
来自 #cint 引脚的外部中断信号
Machine Check
Data Storage Exception
Instruction Storage Exception
External InputIVOR4
来自 #int 引脚的外部中断信号
本文只关注外部中断,完整内容请看处理器手册
从异常类型对应的 IVOR(Interrupt Vector Offset Register) 得到偏移 ( 只取低 16 比特 , 最低 4 比特清零 ),加上 IVPR(Interrupt Prefix Register) 的高 16 比特,构成中断向量的地址:Vector = (IVORn & 0xFFF0) | (IVPR & 0xFFFF0000);值得注意的是,跟经典 PowerPC 不同,Book E 的中断向量是 Effective Address, 对应 Linux 内核的虚拟地址。Linux 对 Book E 中断向量寄存器的设置以 Freescale E500 处理器为例,在 arch/powerpc/kernel/head_xxx.S(xxx 为处理器子类型 ) 中,定义了标签 interrupt_base,紧接着就是各中断向量的处理代码: interrupt_base:
EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE)EXCEPTION 这句展开后如下: .align 5; //32 bytes aligned
ExternalInput:
NORMAL_EXCEPTION_PROLOG;
r3,r1,STACK_FRAME_OVERHEAD;
xfer(0x0500, do_IRQ);这些宏继续展开就是中断处理的代码。后面”中断处理程序”一章有更详细介绍。在 __early_start 函数中,使用以上代码段的地址设置 IVOR4 和 IVPR: li r26, ExternalInput@l; /*IVOR only uses the low 16-bits*/
mtspr SPRN_IVOR4, r26;
r4, interrupt_base@h /*IVPR only uses the high 16-bits*/
SPRN_IVPR, r4以上代码把 IVOR4 和 IVPR 分别设置为 ExternalInput 地址的低 16 位和 interrupt_base 的高 16 位。由于 head_xxx.S 编译出来的目标文件链接在内核镜像的最前面,而内核加载地址对齐在比较大的内存块边界上,所以可以保证 interrupt_base 的高 16 位,跟 ExternalInput 的高 16 位,是相同的。PowerPC 外部中断的屏蔽MSR(Machine Status Register) 的 EE 位 (MSR 的第 16 比特,记为 MSR[EE]) 为 0 时将屏蔽来自 #int 引脚的外部中断,为 1 时使能。mfmsr 和 mtmsr 指令用来读写 MSR 的内容。格式为:"mfmsr rD", "mtmsr rS"。另外 Book E 还提供专门的指令来写 MSR[EE] 位:wrtee 和 wrteei,格式为"wrtee rS","wrteei E"。其中 E 是立即数 0 或 1。Linux kernel 中屏蔽和使能外部中断的接口有: local_irq_disable()/local_irq_enable()
local_irq_save()/local_irq_restore()另外,spin_lock_irq, spin_lock_irqsave 也包含以上屏蔽操作。在 Linux Powerpc 中,以上屏蔽外部中断的操作,都是由内联函数 arch_local_irq_disable 或者 arch_local_irq_save 实现。arch_local_irq_disable 的代码展开如下: #ifdef CONFIG_BOOKE
asm volatile("wrteei 0" : : : "memory");
asm volatile("mfmsr %0\n" : "=r" (rval));
rval &= ~MSR_EE; /*MSR_EE = 1&&16*/
asm volatile("mtmsr %0" : : "r" (rval) : "memory");
#endifPowerPC 中断的硬件处理流程
把正在执行的指令序列下一条指令地址保存到 SRR0(Save/Restore Regiser 0)。
把当前 MSR 的内容保存到 SRR1。
把 MSR 某些比特置为 0,如 PR, EE。注:PowerPC 有两种执行模式,分别为用户模式 (User Mode) 和特权模式 (Supervisor Mode), MSR[PR]=0 表示特权模式。Linux 内核运行在特权模式,而普通程序运行在用户模式。
在新的 MSR 状态下,从中断向量偏移处开始指令读取和执行。
外部中断处理结束时,必须通过 rfi 指令返回。rfi 的执行,会把 SRR1 的内容恢复到 MSR, 并从 SRR0 所保存的地址处继续执行。PowerPC Linux 中断处理程序异常向量 ExternalInput 处的处理程序主要分为以下几个步骤:
NORMAL_EXCEPTION_PROLOG 宏
建立用户中断处理程序的栈帧,并把一些寄存器的值保存在栈帧中,它们在栈帧中布局由 struct pt_regs 定义。
执行 do_IRQ
在 irq_enter() 函数中更新 preempt_count。 读 MPIC 的 IACK 寄存器,通知 MPIC 开始处理该中断,同时获取 MPIC 中断号 , 映射为软件中断号,找到并运行注册在该软件中断号上的用户中断处理程序,最后写 MPIC 的 EOI 寄存器,通知 MPIC 中断处理结束。在 irq_exit() 中更新 preempt_count, 调用 invoke_softirq() 处理 pending 的软中断。
ret_from_except
某些条件满足时,进行进程调度和信号处理,最后调用 rfi 指令返回。中断控制器在什么情况下把外设的中断信号发送给处理器?读 IACK 寄存器得到的是什么?硬件中断号怎么来的?为什么要写 EOI ?中断嵌套?这些问题的答案并不存在于代码中,需要了解 MPIC 的内部逻辑和编程接口才能回答。MPICMPIC 概述OpenPIC(OPEN Programmable Interrupt Controller) 是 AMD 和 Cyrix 联合开发的中断控制器规范。目前 PowerPC 系统多使用 MPIC (Multi-Processor Interrupt Controller)。MPIC 兼容 OpenPIC 并有所增强,主要包括增加了 #cint 和 #mcp 中断信号输出,更灵活的多处理器中断路由算法等。PowerPC 处理器与 MPIC 的连接,中断信号的路由MPIC 的主要功能就是接受外设的中断信号或者 MPIC 本身产生的中断,按照一定的逻辑和次序,向各个处理器的管脚提交中断信号。MPIC 本身也会产生中断,包括 IPI(Inter Processor Interrupt), MSI(Message Signaled Interrupt), MPIC Timer Interrupt 等。这些中断最后也是由 MPIC 送到处理器的 #int 引脚。对于处理器来说,它们也都是外部中断。关于这些中断及其使用,将另文介绍。中断源编号无论是从 MPIC 外部引脚进来的中断,还是 MPIC 自身产生的中断,都有一个中断源编号,范围是 0~127。查阅芯片手册可以获得具体中断的中断源编号。MPIC 编程接口中断源配置寄存器每个中断源对应一套配置寄存器。它们的地址由中断源编号 n 决定。VPRn = VPR0 + 32 * DRn = VPRn + 16;
VPR(Vector Priority Register), 中断向量优先级寄存器。包括 16 比特 Vector ( 即所谓硬件中断号,本文称为 MPIC 中断号 ),4 比特优先级和 1 比特屏蔽位,还有两个比特用来设置中断信号的极性与模式 ( 组合为上升缘,下降缘,高电平,低电平四种触发方式 )。中断源编号到 MPIC 中断号的映射,是通过对 VPR 编程来控制的。
DR(Destination Register) 中断目的寄存器。P0~Pm 位用来指定该中断被定向到哪个处理器。注意:对于 IPI 和 MPIC Timer 中断,DR 有多个比特被置 1,表示多播。一个中断被复制到多个处理器; 而对于来自外设的中断,DR 有多个比特被置 1,表示分布式分发,由 MPIC 进行路由选择,只将该中断发送给其中一个处理器。Freescale 的 QorIQ 系列平台的 MPIC, 都不支持分布式分发模式。Per CPU 寄存器每个处理器对应一套 Per CPU 寄存器。以下计算地址的公式中,n 代表处理器编号。
CTPR(Current Task Priority Register)地址为:CTPRn = MPIC_CPU_BASE+ 4096*n + 0x80包含 4 比特优先级字段。OS 可以设置当前任务的优先级,只有中断源的优先级大于 CTPR,MPIC 才会向处理器提交中断信号。对于实时操作系统来说,这是一个非常友好的设计。不过 Linux 目前没有利用这一功能,所有 CTPR 都一直设为 0。注意:中断源优先级等于 CTPR 时,不会被提交到处理器。CTPR 最小值是 0,所以把中断的优先级设为 0,相当于屏蔽该中断。
IACK(Interrupt Ack Register)地址为:IACKn = MPIC_CPU_BASE+ 4096*n + 0xa0只读。处理器 n 响应一个外部中断,读 IACKn 就可以得到该中断的 MPIC 中断号 (Vector/Priority 寄存器的 Vector 字段 )。
EOI(End Of Interrupt)地址为 EOIn = MPIC_CPU_BASE+ 4096*n + 0xb0写 0 表示结束一个中断的处理。全局寄存器如 GCR 寄存器可以重启 MPIC, PIR 寄存器可以重启指定的处理器。Linux 内核 MPIC 寄存器的定义powerpc/include/asm/mpic.h 文件有以上寄存器地址偏移和各字段的比特掩码定义MPIC 内部寄存器和流程另外还有几个 MPIC 内部的 Per CPU 寄存器,一般处理器无法访问它们,但是它们在 MPIC 内部处理流程中扮演了非常重要的角色。
IPR(Interrupt Pending Register)共 136 位,分别代表 136 个中断源。IPR 记录 MPIC 接收到中断信号,但还没有开始处理的中断。
IRR(Interrupt Request Register)IRR 只保存一个中断源编号。这个中断一定是 IPR 中优先级最高的,并且比处理器正在处理的中断 ( 如果有的话 ) 的优先级更高。
ISR(In-Service Register)15 位。ISR 记录处理器开始处理 ( 读 IACK),但还没有处理完的中断 ( 写 EOI) 的优先级。MPIC 不允许优先级相同的中断互相嵌套,所以最多能嵌套的中断数就是有效优先级的数目 15。MPIC 接收到一个中断信号以后,如果中断源寄存器的屏蔽位为 0,则根据 DR 的内容,将 IPR 对应比特置 1。例如,若 DR 的 P2 被置 1,表示该中断被定向到处理器 2,因此将 IPR2 的对应比特置 1。IPR 中优先级最高的中断,如果其优先级大于 ISR 中的最高优先级,则将它的中断源编号,保存到 IRR,同时它的 MPIC 中断号被拷贝到 IACK 中。如果 IRR 中断的优先级大于 CTPR,则向处理器 #int 引脚输出中断信号。( 注意此时有可能有其他中断正在处理中 )如果处理器的 MSR[EE] 位为 1,则执行 ExternalInput 中断向量,读 IACK 得到 MPIC 中断号。对 IACK 的读操作,同时会翻转中断信号的电平极性;将 ISR 中的对应比特置 1,表示该级别中断正在被处理中;IPR 中对应的比特也会被清零。中断处理程序执行完时,向 EOI 写 0。MPIC 将 ISR 中优先级最高的比特清零。总结一下:只有屏蔽位为零的中断,才能进入到 IPR。只有更高优先级的中断都处理完之后,IPR 中的中断才能进入到 IRR。只有 IRR 中断的优先级大于 CTPR 时,才能向处理器输出中断信号。只有处理器的 MSR[EE] 位为 1,处理器才响应中断信号,执行外部中断向量。一个中断存在于 ISR 中的时间范围是软件读 IACK 和写 EOI 之间。ISR 的行为类似一个堆栈,并且压入堆栈的总是更高的优先级,弹出的总是当前最高优先级。实际上,在系统内存中的确有一个这样的堆栈跟 ISR 相对应,那就是中断处理程序嵌套而形成的堆栈。要支持中断嵌套,需要在中断处理程序中把 MSR[EE] 设为 1。( 因为在进入中断向量之前,MSR[EE] 被处理器自动设置为 0)对 PowerPC Linux 来说,在中断处理过程中,软件跟 MPIC 之间的交互就是读 IACK 和写 EOI。他们的实现请参考:读 IACK do_IRQ
ppc_md.get_irq()
==mpic.c::mpic_get_irq()写 EOI do_IRQ
handle_one_irq()
generic_handle_irq()
desc-&handle_irq() = handle_xxxx_irq()
desc-&irq_data.chip-&irq_eoi()
==mpic.c::mpic_eoi()中断号的映射有三种关于中断的序号:
中断源编号由物理连接决定。它也是中断源配置寄存器的地址索引。
MPIC 中断号VPR 寄存器 Vector 字段的值,是由软件配置到 VPR 的,理论取值范围为 0~65535。Linux 配置 VPR 时,总是让 Vector 等于中断源编号。
软件中断号在 Linux 中,一个外部中断用 irq_desc 结构来表示,用同名的数组 irq_desc[NR_IRQS] 来表示所有外部中断。这个数组的索引就是软件中断号。软件中断号跟 MPIC 中断号往往并不相等。函数 irq_create_mapping 用来建立 MPIC 中断号到软件中断号的映射。软件中断号到 MPIC 中断号的映射保存在数组 irq_map 中。virq_to_hw 宏用来从软件中断号得到 MPIC 中断号。反向映射 ( 从 MPIC 中断号得到软件中断号 ) 有四种方式,分别是:
IRQ_HOST_MAP_LEGACY两者取值相同
IRQ_HOST_MAP_NOMAP两者并不相等,但是没有维护反向映射表。只能遍历 irq_map 来进行反向映射。
IRQ_HOST_MAP_LINEAR用数组 revmap 保存反向映射表。查询函数为 irq_linear_revmap。
IRQ_HOST_MAP_TREE用 radix tree 来保存反向映射表。查询函数为 irq_radix_revmap_lookup
寻找为 Linux 开发人员(包括 )准备的更多参考资料,查阅我们 。 在 developerWorks 上查阅所有
和 。 随时关注 developerWorks 和。 Linux PowerPC 详解核心篇 , 王齐。 ,作者: 。加入 ,developerWorks 社区是一个面向全球 IT 专业人员,可以提供博客、书签、wiki、群组、联系、共享和协作等社区功能的专业社交网络社区。加入 ,参与在线交流。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
为灾难恢复构建应用,赢取现金大奖。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=LinuxArticleID=821179ArticleTitle=MPIC 与 PowerPC Linux 中断处理publish-date=

我要回帖

更多关于 powerpc架构 的文章

 

随机推荐