1. 本文简介
本文主要介绍P2020芯片中vxWorks中断初始化过程(
部分讲解是以linux为例)。P2020属于PPC85XX系列,内核为e500v2,它是PowerPC体系结构中主要应用于通信领域的片子,PowerPC体系结构规范发布于1993年,它是一个64位规范(也包含32位子集)。但几乎所有常规的PowerPC的芯片都是32位的,除了部分新型高端芯片(eg:IBM RS/6000等)。
本文主要章节如下:
2 参考书籍介绍
3 OEA相关寄存器(中断相关)
4 PowerPC架构异常处理机制(P2020为代表)
5 vxWorks中断初始化过程
6 总结
2.参考书籍
(1) IBM官方文档,关于PowerPC Architecture Boook I II III ,其中Book III 主要讲解与操作系统相关的寄存器。
下载链接:
https://www.ibm.com/developerworks/systems/library/es-archguide-v2.html.
(2) <
> 该手册描述的是e500核的工作过程以及Reg详细描述,包括电源管理,中断处理,Cache,MMU.
(3) <> 该手册主要描述的是芯片与外设相关的控制器,但很少介绍到CPU通用寄存器,特殊寄存器等.
(4) <<821INSTSET.pdf>> PowerPC 汇编指令集介绍
(5) PowerPC 汇编指令简单介绍
(2.3.4)都可以在NXP官网中下载
3 OEA相关寄存器(中断相关)
PowerPC 处理器有 32 个(32 位或 64 位)GPR(通用寄存器)以及诸如 PC(程序计数器,也称为 IAR/指令地址寄存器 或 NIP/下一指令指针)、LR(链接寄存器)、CR(条件寄存器)等各种其它寄存器。有些 PowerPC CPU 还有32个64位的FPR(浮点寄存器). 下图展示了Power ISA2.04寄存器模式,且值得注意的是 PowerPC有两种执行模式,分别为用户模式(UserMode) 和特权模式(SupervisorMode),MSR[PR]=0 表示特权模式。Linux内核运行在特权模式,而普通程序运行在用户模式。VxWorks全部运行在特权模式。
3.1 OEA寄存器集包括四类寄存器(具体介绍只涉及中断相关的,其它请参考下来的链接)
本小章节参考:32位PowerPC构架通用寄存器分析及总结
(1)配置寄存器(Configuration Registers)
MSR寄存器:(与中断息息相关)
定义处理器的状态,它可以被mtmsr, sc, rfi指令修改;可以被mfmsr读取;
详细说明:基于603E内核的PowerPC处理器,中断向量为一个固定的地址,可以通过设置MSR[IP]位来决定这个固定地址是0x0,还是0xfff0_0000;而E500内核在进入中断和异常处理程序时,不能关闭MMU,因此不能使用物理地址作为中断向量,而应使用IVPR和IVOR寄存器保存相应的中断向量(在下面中断机制章节详细介绍),下面为e500核的MSR:
中断的开关由寄存器MSR来控制的
CE:if set 1,Critical input and watchdog timer interrupts are enabled.
EE:if set 1,External input, decrementer, fixed-interval timer and performance monitor interrupts are enabled
ME:if set 1, Machine check interrupts are enabled.
DE:if set 1,Debug interrputs are enabled if DBCR0[IDM] = 1.
MSR[EE]这个bit很重要,中断的使能要靠它。
注意:当发生中断后,MSR[EE]会自动置0屏蔽所有其他的中。
如果在MSR[EE]置1前,又来了一个中断
1.此中断是边沿触发的,那么此中断丢失。此种中断就是电平变化,一次就没了
2.此中断时水平触发的,此中断不会丢失。此种中断的电平会一直active,直到硬件处理它
PVR寄存器:
定义寄存器模型的版本和处理器的版
(2)内存管理寄存器(Memory Management Registers)
BAT寄存器:
OEA定义了四组BAT指令寄存器(IBAT0U-IBAT3U和IBAT0L-IBAT3L),也定义了四组BAT数据寄存器(DBAT0U-DBAT3U和DBAT0U-DBAT3U)
SDR1寄存器:
该寄存器定义了用于虚拟地址转换为物理地址所需要的页表基地址
SR寄存器:
OEA定义了16个32位的SR寄存器(SR0-SR15)
(3)中断处理寄存器(Interrupt Handing Register)
详细介绍请看《E500CORE.pdf》2.7 Interrupt Registers
(4)多功能寄存器(Miscellaneous Registers)
TB(time base)寄存器:CPU时间片基准
DEC寄存器:这是一个32位的递减计数器
EAR(External Access Register)寄存器:用于访问外部设备
DABR(Data address breakpoint register):用来控制数据地址断点功能
PIR(Processor identification register):在多处理器的芯片上用来标识一个核,例如在MPC8641d芯片上有两个E600的core,就用PIR来定位其中的core。
3.3 通用寄存器与专用寄存器的用途
本小章节参考:PowerPC汇编指令以及通用与专用寄存器介绍
r3,r4-r10经常用于C,汇编的混合编程中
在targetharchppc oolPpc.h中有对以上寄存器的重定义
4 PowerPC架构异常处理机制(P2020为代表)
4.1 PowerPC中断系统(P2020)
从CPU的角度来讲,中断源可以分为自己内核产生的异常和PIC提供的中断。
异常:是e500核产生的,它是同步产生(可以预知的),如非法指令,或访问存储器时出现TLB Miss(Data | Instruction )等.
<>5.11.1描述了PowerPC架构可能出现的全部异常(这里描述包括中断)
中断:是e500核外部引脚产生的,由PIC送到内核处理,它是异步产生的(无法预知的).大致分为一般中断(int)(包括内外部等),关键中断(cint)和机器检查中断(machine check). 下图为中断源架构图,其中Message数量应该是写错了(理论为4).
从图中可以看出PowerPC处理器中断系统由内核中断以及异常处理系统和PIC中断控制器构成.
4.2 中断向量
在E500内核中,使用IVPR和IVORx寄存器共同确定中断或者异常程序的入口地址。其中,IVPR寄存器提供中断程序入口地址的第0~15位,IVORx提供中断程序入口地址的第16~27位,而中断程序的入口地址的第28~31位为0。IVORx与异常的对应关系如下图所示:
每类中断都有自己的中断向量,通过它能计算出中断handler的指令地址。
指令地址的计算方法:
IVPR[32-47] || IVORn[48-59] || 0b0000
这样就得到一个32个bit地址
有2个中断向量对程序员来说比较亲切:
一个是 IVOR4 用来处理外部中断和内部SOC中断
一个是 IVOR8 用来处理系统调用
随便找一个Start.S进行简单的分析
185 /* Setup interrupt vectors 设置IVPR寄存器*/
186 lis r1,TEXT_BASE@h /* load value to r1 */
187 mtspr IVPR, r1 /* write r1 to IVPR */
197 li r1,0x0500
198 mtspr IVOR4,r1 /* 4: External interrupt */
.......
205 li r1,0x0900
206 mtspr IVOR8,r1 /* 8: System call */
207 /* 9: Auxiliary processor unavailable(unsupported) */
208 li r1,0x0a00
209 mtspr IVOR10,r1 /* 10: Decrementer */
......
看board/pq37pc/pq37pc_8560/config.mk 里有
TEXT_BASE = 0xFF800000
所以 IVPR = 0xFFF80000
IVOR10 = 0x00000a00
计算 IVPR[32-47] || IVPOR10[48-59] || 0b0000 得 0xFFF80a00
4.3 中断源寄存器
中断向量只能确定类型,里面保存了interrupt handler的地址。
在interrupt handler里,需要根据中断源寄存器来进一步确定到底是哪里发生了中断
E500内部中断源有64个(部分reserve),来自TSEC,LBC,DMA,CPM等
寄存器组 PIC_IIVPRn 与 P2020内部SOC中断一一对应(n取值 0~63)
E500外部中断源有12个,来自外部引脚 IRQ[0:11]
寄存器组 EIVPRn 与 MPC8560 外部中断一一对应(n取值 0~11)
下面具体介绍外部源寄存器
MSK: 若置1,此中断源的中断被无视.
A: 若置1,此中断源有中断发生
P: 若置1, active-high; 若置0, active-low.
S: 若置1,此中断是水平触发。若置0,此中断是边界触发
PRIORITY: 优先级0-15。15是最高优先级,0时相当于无视此中断。
VECTOR: 硬件中断号。中断发生后处于pengding时,此字段被写在寄存器IACK中
总结:处理内,外中断的handle都是同一个functions(流程),EntInt-->Handle-->ExitInt,在处理函数中读取IACK,就知道是哪个中断源触发了中断,然后做对应的处理
知道中断向量 和 中断源,系统处理中断的大致流程就能理出来了,以外部中断eg:
4.4 外部中断处理流程
1.首先E500内核清除在指令完成队列CQ中所有的执行,然后把正在执行的指令序列下一条指令地址保存到中断寄存器 SRR0(Save/RestoreRegiser 0)
2.把当前 MSR 的内容保存到 SRR1
3.把 MSR 某些比特置为 0,如MSR[SPE,WE,EE,PR,FP,FE0,FE1,IS,DS]are 0 by all interrupts.
(E500内核将MSR寄存器的CE、ME、DE位保留,其他位全部清零。因此E500内核在进行外部中断处理程序时,仍然可以被Critical中断,Machine check中断和调试中断程序重入,但是不能被外部中断立即重入。在Linux PowerPC中,外部中断处理程序会选择合适时机使能MSR寄存器的EE位,以支持外部中断的重入)
4.在新的 MSR 状态下, E500内核将根据IVPR,IVOR4寄存器确定中断向量,从中断向量偏移处开始指令读取和执行
5. 在中断处理程序执行完毕后,使用rfi指令进行中断返回。rfi指令将从SRR1寄存器中恢复MSR寄存器的值,并从SRR0寄存器中获得程序返回地址。rfi指令在进行程序正文切换之前还会进行指令和数据的同步,还给被中断的程序一个“干净”的空间,之后E500内核进行中断返回。
5.vxWorks中断初始化过程(跳过bootrom过程)
usrInit(prjConfig.c)--> {sysStart(startType)-->intVecBaseSet ((FUNCPTR *) VEC_BASE_ADRS)}
-->excVecInit()
(1)sysStart (first C code executed from usrInit) 主要作用是清除BSS,以及设置中断向量的基地址
Code in argetconfigcompssrcusrStartup.c
中断向量的基地址一般设置为0x0000_0000 | 0xfff0_00000
macro in BSP/config.h
#define VEC_BASE_ADRS LOCAL_MEM_LOCAL_ADRS
#define LOCAL_MEM_LOCAL_ADRS 0x00000000
void sysStart(int startType){
#if (CPU_FAMILY == PPC) || (CPU_FAMILY == MIPS)
/*
* For PPC and MIPS, the call to vxSdaInit() must be the first operation
* in sysStart(). This is because vxSdaInit() sets the SDA registers
* (r2 and r13 on PPC, gp on MIPS) to the SDA base values. No C code
* must be placed before this call.
*/
_WRS_ASM (""); /* code barrier to prevent compiler moving vxSdaInit() */
vxSdaInit (); /* this MUST be the first operation in usrInit() for PPC */
_WRS_ASM (""); /* code barrier to prevent compiler moving vxSdaInit() */
#endif /* (CPU_FAMILY == PPC) || (CPU_FAMILY == MIPS) */
...
#ifdef CLEAR_BSS
bzero (edata, end - edata);
#endif /* CLEAR_BSS */
sysStartType = startType;
intVecBaseSet ((FUNCPTR *) VEC_BASE_ADRS);
...
#ifdef _WRS_CONFIG_USE_MEMDESC
sysMemDescInit ();
#endif
}
(2) intVecBaseSet
Code in targetsrcarchppcintArchLib.c
说明:此阶段函数并未真正设置中断向量基地址到e500内核中,只是对_ppcExcIntVecBase赋值的
过程,实际设置的过程在excVecInit里面。
int (* _func_intLevelSetRtn) (int) = NULL;
int (* _func_intEnableRtn) (int) = NULL;
int (* _func_intDisableRtn) (int) = NULL;
上面的函数指针初始化在vxbEpicIntCtlr.c里面
int intEnable
(
int intLevel /* interrupt level to enable */
)
{
/* execute VxBus Legacy interrupt enable routine first if supported */
if ((_func_vxbIntEnable != NULL) &&
(_func_vxbIntEnable (intLevel) != ERROR))
return (OK);
if (_func_intEnableRtn != NULL)
return (_func_intEnableRtn (intLevel));
return (ERROR);
}
void intVecBaseSet(FUNCPTR * baseAddr){
/* 主要_func_intVecBaseSetRtn实际为空,在vxWorks目前阶段函数指针未赋值
** 可在vxWorks shell 中 _func_intVecBaseSetRtn == 1 测试返回值
*/
if (_func_intVecBaseSetRtn != NULL)
_func_intVecBaseSetRtn (baseAddr);
_ppcExcIntVecBase = baseAddr;
_ppcAllocationQuantumSize = _CPU_ALLOC_ALIGN_SIZE;
_ppcStackAlignSize = _CPU_STACK_ALIGN_SIZE;
...
}
FUNCPTR * intVecBaseGet (void)
{
if (_func_intVecBaseGetRtn == NULL)
return (_ppcExcIntVecBase);
return (_func_intVecBaseGetRtn ());
}
(3)excVecInit
Code in targetsrcarchppcexcArchLib.c
STATUS excVecInit (void)
{
FAST int ix;
#if !defined(_WRS_CONFIG_WRHV_GUEST) || defined(_VB_PISA_EHV)
if (excExtendedVectors == TRUE)
{
entOffset = EXT_ENT_OFF;
isrOffset = EXT_ISR_OFF;
exitOffset = EXT_EXIT_OFF;
#ifdef _EXC_OFF_CRTL
...
#endif /* _EXC_OFF_CRTL */
}
else
{
...
}
excVecBaseSet(intVecBaseGet());
for (ix = 0; excBlTbl[ix].excHandler != (void (*)()) NULL; ix++)
{
excVecConnectCommon (excBlTbl[ix].vecOff,
excBlTbl[ix].vType,
excBlTbl[ix].excHandler,
excBlTbl[ix].vecOffReloc);
}
#ifndef _WRS_CONFIG_WRHV_GUEST
#ifdef IVOR0
excIvorInit();
#endif /* IVOR0 */
#endif /* _WRS_CONFIG_WRHV_GUEST */
...
/*
* Now that the vectors are set up, and provided we can safely do
* so prior to MMU setup, set the recoverability indicator if so
* equipped. (If excVecBase and excVecBaseAltAdrs differ,
* no exceptions can be handled until the MMU has been set up.)
* We don't enable Machine Check exceptions here because excHandler
* is not ready, i.e. taskIdCurrent is a meaningless value now.
* We postpone it to usrRoot and use taskMsrDefault to enable it.
*/
if (excVecBaseAltAdrs == excVecBase)
vxMsrSet (vxMsrGet()
#ifdef _PPC_MSR_RI
| _PPC_MSR_RI
#endif /* _PPC_MSR_RI */
);
#ifndef _WRS_CONFIG_WRHV_GUEST
/* Used for the generic layered exception handler */
hdlrBase = excVecBase + _EXC_OFF_END;
/* save the Data and/or Instruction MMU selected */
hdlrCodeBase = excVecBaseAltAdrs + _EXC_OFF_END;
#if (CPU==PPC85XX)
# if !defined(PPC_e200) && !defined(PPC_e500mc) || defined(PPC_e6500)
installE500ParityErrorRecovery();
# endif /* !PPC_e200 && !PPC_e500mc || PPC_e6500 */
#endif /* (CPU==PPC85XX) */
#endif /* _WRS_CONFIG_WRHV_GUEST */
#else /* _WRS_CONFIG_WRHV_GUEST && !_VB_PISA_EHV */
{
char * addr;
int excSize = (int)&_func_exc_handler_end - (int)&_func_exc_handler;
int intSize = (int)&_func_int_handler_end - (int)&_func_int_handler;
bzero ((void *)0, _EXC_OFF_END);
for (addr = 0; addr < (char *)_EXC_OFF_END; addr+=0x100)
{
bcopy ((void *)&_func_exc_handler, (void *)addr, excSize);
CACHE_TEXT_UPDATE ((void *)addr, excSize);
}
/* Set interrupt handlers */
bcopy ((void *)&_func_int_handler, (void *)_EXC_OFF_INTR, intSize);
CACHE_TEXT_UPDATE ((void *)_EXC_OFF_INTR, intSize);
#ifdef _EXC_OFF_DIRECT_INTR
bcopy ((void *)&_func_int_handler, (void *)_EXC_OFF_DIRECT_INTR, intSize);
CACHE_TEXT_UPDATE ((void *)_EXC_OFF_DIRECT_INTR, intSize);
#endif /* _EXC_OFF_DIRECT_INTR */
#ifdef _EXC_OFF_DECR
bcopy ((void *)&_func_int_handler, (void *)_EXC_OFF_DECR, intSize);
CACHE_TEXT_UPDATE ((void *)_EXC_OFF_DECR, intSize);
#endif /* _EXC_OFF_DECR */
}
#endif /* _WRS_CONFIG_WRHV_GUEST && !_VB_PISA_EHV */
return (OK);
}
下面按excVecInit里面函数的初始化顺序讲解:
macro in targetharchppcprivateexArchLibP.h
# define EXT_ENT_OFF 3 /* offset for ext intEnt/excEnt */
# define EXT_ISR_OFF 8 /* offset for ext ISR or exc handler */
# define EXT_EXIT_OFF 15 /* offset for ext intExit/excExit */
描述的是在每类中断向量里面中断入口函数,处理函数,退出函数相对于这类向量地址的偏移量,目前我还没理解值为啥是这个数。
(1)excVecInit里面有:
entOffset = EXT_ENT_OFF;
isrOffset = EXT_ISR_OFF;
exitOffset = EXT_EXIT_OFF;
(2)excVecInit 调用 excVecBaseSet(intVecBaseGet()); 此时才真正设置了中断向量基地址
void excVecBaseSet(FUNCPTR * baseAddr){
...
excVecBase = (vectorBase)((uint32_t)baseAddr & 0x0ffff0000);
vxIvprSet ((int) excVecBase);
...
}
/* code in vxALib.s */
FUNC_BEGIN(vxIvprGet)
mfspr p0, IVPR
blr
FUNC_END(vxIvprGet)
FUNC_BEGIN(vxIvprSet)
mtspr IVPR, p0
blr
FUNC_END(vxIvprSet)
(3)excVecConnectCommon 该程序是安装所有中断默认处理的函数
函数涉及vxWorks中一个重要的数据结构异常向量表excBlTbl[]
typedef struct excTbl
{
vecTblOffset vecOff; /* vector offset */
EXC_TYPE vType; /* exception type */
void (*excHandler) (); /* exception handler routine */
vecTblOffset vecOffReloc; /* relocated vector offset */
} EXC_TBL;
vecTblOffset 为UINT32,在此代表的意义为相对于中断向量表基地址的偏移量(简单来说就是vecOff用来设置IVORs寄存器的,当然基地值为0的情况下)
异常向量表的表项数量和内容随CPU不同而不同,对于P2020处理器而言,其异常向量表中的表项如下:
{
{_EXC_OFF_CRTL, V_CRIT_INT, excIntHandle, 0}, /* critical int */
#ifdef _PPC_MSR_MCE
{_EXC_OFF_MACH, V_MCHK_EXC, excExcHandle, 0}, /* machine chk */
#elif _PPC_MSR_CE
{_EXC_OFF_MACH, V_CRIT_EXC, excExcHandle, 0}, /* machine chk */
#endif /* _PPC_MSR_MCE */
{_EXC_OFF_DATA, V_NORM_EXC, excExcHandle, 0}, /* data storage */
{_EXC_OFF_INST, V_NORM_EXC, excExcHandle, 0}, /* instr access */
{_EXC_OFF_INTR, V_NORM_INT, excIntHandle, 0}, /* ext int */
#ifdef _EXC_OFF_DIRECT_INTR
{_EXC_OFF_DIRECT_INTR, V_NORM_INT, excIntHandle, 0}, /* ext int */
#endif /* _EXC_OFF_DIRECT_INTR */
{_EXC_OFF_ALIGN, V_NORM_EXC, excExcHandle, 0}, /* alignment */
{_EXC_OFF_PROG, V_NORM_EXC, excExcHandle, 0}, /* program */
{_EXC_OFF_FPU, V_NORM_EXC, excExcHandle, 0}, /* fp unavail */
{_EXC_OFF_SYSCALL, V_NORM_EXC, excExcHandle, 0}, /* syscall */
{_EXC_OFF_APU, V_NORM_EXC, excExcHandle, 0}, /* auxp unavail*/
{_EXC_OFF_DECR, V_NORM_INT, excDecrHandle, 0}, /* decrementer */
{_EXC_OFF_FIT, V_NORM_INT, excIntHandle, 0}, /* fixed timer */
{_EXC_OFF_WD, V_CRIT_INT, excIntHandle, 0}, /* watchdog */
{_EXC_OFF_DATA_MISS, V_NORM_EXC, excExcHandle, 0}, /* data TLB miss */
{_EXC_OFF_INST_MISS, V_NORM_EXC, excExcHandle, 0}, /* inst TLB miss */
{_EXC_OFF_DBG, V_CRIT_EXC, excExcHandle, 0}, /* debug events */
#ifdef _WRS_ALTIVEC_SUPPORT
{_EXC_ALTIVEC_UNAVAILABLE, V_NORM_EXC,excExcHandle, 0}, /* altivec unav */
{_EXC_ALTIVEC_ASSIST, V_NORM_EXC, excExcHandle, 0}, /* altivec asst */
#endif /* _WRS_ALTIVEC_SUPPORT */
#ifdef _WRS_SPE_SUPPORT
{_EXC_OFF_SPE, V_NORM_EXC, excExcHandle, 0}, /* SPE */
{_EXC_OFF_VEC_DATA, V_NORM_EXC, excExcHandle, 0}, /* vector data */
{_EXC_OFF_VEC_RND, V_NORM_EXC, excExcHandle, 0}, /* vector round */
#endif /* _WRS_SPE_SUPPORT */
{_EXC_OFF_PERF_MON, V_NORM_INT, excIntHandle, 0}, /* perf monitor */
}
表格中涉及的宏targetharchppcexcPpcLib.h
macro in
#define _EXC_OFF_CRTL 0x0100 /* Critical Input */
#define _EXC_OFF_MACH 0x0200 /* Machine Check */
#define _EXC_OFF_DATA 0x0300 /* Data Storage */
#define _EXC_OFF_INST 0x0400 /* Instruction Storage */
#define _EXC_OFF_INTR 0x0500 /* External Input */
#define _EXC_OFF_ALIGN 0x0600 /* Alignment */
#define _EXC_OFF_PROG 0x0700 /* Program */
#define _EXC_OFF_FPU 0x0800 /* Floating Point Unavailable */
#define _EXC_OFF_SYSCALL 0x0900 /* System Call */
#define _EXC_OFF_APU 0x0a00 /* Auxiliary Processor Unavailable */
#define _EXC_OFF_DECR 0x0b00 /* Decrementer */
#define _EXC_OFF_FIT 0x0c00 /* Fixed Interval Timer */
#define _EXC_OFF_WD 0x0d00 /* Watchdog Timer */
#define _EXC_OFF_DATA_MISS 0x0e00 /* Data TLB Error */
#define _EXC_OFF_INST_MISS 0x0f00 /* Instruction TLB Error */
#define _EXC_OFF_DBG 0x1000 /* Debug exception */
typedef enum excType
{
V_NORM_EXC = 0,
V_NORM_INT /* 重点对象 */
#ifdef _PPC_MSR_CE
,V_CRIT_EXC
,V_CRIT_INT
#ifdef _PPC_MSR_MCE
,V_MCHK_EXC
#endif /* _PPC_MSR_MCE */
#endif /* _PPC_MSR_CE */
} EXC_TYPE;
/* 中断的入口函数以及出口函数 */
LOCAL EXC_WRAPPERS excTypeRtnTbl[] =
{
{excEnt, excExit}, /* V_NORM_EXC */
{intEnt, intExit}, /* V_NORM_INT */
#ifndef _VB_PISA_EHV
#ifdef _PPC_MSR_CE
{excCrtEnt, excCrtExit}, /* V_CRIT_EXC */
{intCrtEnt, intCrtExit}, /* V_CRIT_INT */
#ifdef _PPC_MSR_MCE
{excMchkEnt, excMchkExit} /* V_MCHK_EXC */
#endif /* _PPC_MSR_MCE */
#endif /* _PPC_MSR_CE */
#else /* _VB_PISA_EHV */
/* XXX need spurious exception handler */
# ifdef _PPC_MSR_CE
{NULL, NULL}, /* V_CRIT_EXC */
{NULL, NULL}, /* V_CRIT_INT */
# ifdef _PPC_MSR_MCE
{NULL, NULL} /* V_MCHK_EXC */
# endif /* _PPC_MSR_MCE */
# endif /* _PPC_MSR_CE */
#endif /* _VB_PISA_EHV */
};
总的说来:excVecConnectCommon 是对中断向量表做处理,根据中断向量表第一个参数vecoff,计算该类中断的内存地址位置,然后将存根机器代码拷贝到该位置上,将xxEnt,xxExit,handler放置各自对应的位置中
/* copy the stub to the vector location */
bcopy((char *)stub, (char *)cVec, stubSize);
(4)excIvorInit 设置IVOR寄存器
macro in excPpcLib.h
/* Mappings between vector names and corresponding IVORs, for excALib.s */
#define IVOR0_VAL _EXC_OFF_CRTL /* Critical Input */
#if ((defined PPC_440x5) || (CPU == PPC465))
#define IVOR1_VAL _EXC_OFF_MCRECOV /* recoverable Machine Check */
#else /* PPC_440x5 || PPC465 */
#define IVOR1_VAL _EXC_OFF_MACH /* Machine Check */
#endif /* PPC_440x5 || PPC465 */
#define IVOR2_VAL _EXC_OFF_DATA /* Data Storage */
#define IVOR3_VAL _EXC_OFF_INST /* Instruction Storage */
#define IVOR4_VAL _EXC_OFF_INTR /* External Input */
#define IVOR5_VAL _EXC_OFF_ALIGN /* Alignment */
#define IVOR6_VAL _EXC_OFF_PROG /* Program */
#define IVOR7_VAL _EXC_OFF_FPU /* Floating Point Unavailable */
#define IVOR8_VAL _EXC_OFF_SYSCALL /* System Call */
#define IVOR9_VAL _EXC_OFF_APU /* Auxiliary Processor Unavailable */
#define IVOR10_VAL _EXC_OFF_DECR /* Decrementer */
#define IVOR11_VAL _EXC_OFF_FIT /* Fixed Interval Timer */
#define IVOR12_VAL _EXC_OFF_WD /* Watchdog Timer */
#define IVOR13_VAL _EXC_OFF_DATA_MISS /* Data TLB Error */
#define IVOR14_VAL _EXC_OFF_INST_MISS /* Instruction TLB Error */
#define IVOR15_VAL _EXC_OFF_DBG /* Debug exception */
code in . argetsrcarchppcexcALib.s
FUNC_EXPORT(excIvorInit)
FUNC_BEGIN(excIvorInit)
li p0, IVOR0_VAL
mtspr IVOR0, p0
li p0, IVOR1_VAL
mtspr IVOR1, p0
li p0, IVOR2_VAL
mtspr IVOR2, p0
li p0, IVOR3_VAL
mtspr IVOR3, p0
li p0, IVOR4_VAL
...
FUNC_END(excIvorInit)
存根代码表:
LOCAL INSTR excConnectCode[]=
{
/* data word byte opcode operands */
0x7c7343a6, /* 0 0x00 mtspr SPRG3, p0 */
#if defined(_EXC_OFF_CRTL)
# if defined(T4_ERRATUM_CPU6198) && defined(_WRS_CONFIG_SMP)
0x7c6000a6, /* 1 0x04 mfmsr p0 */
0x546303da, /* 2 0x08 rlwinm p0,p0,0,15,13 clear MSR[CE] */
0x7c7b8ba6, /* 3 0x0c mtspr MCSRR1,p0 */
0x3c600000, /* 4 0x10 lis p0,HI(mtmsrwa) */
0x60630000, /* 5 0x14 ori p0,p0,LO(mtmsrwa) */
0x7c7a8ba6, /* 6 0x18 mtspr MCSRR0,p0 */
0x4c00004c, /* 7 0x1c rfmci */
# else
0x7c6000a6, /* 1 0x04 mfmsr p0 */
0x546303da, /* 2 0x08 rlwinm p0,p0,0,15,13 clear MSR[CE] */
0x7c600124, /* 3 0x0c mtmsr p0 */
0x60000000, /* 4 0x10 nop */
# endif /* T4_ERRATUM_CPU6198 && _WRS_CONFIG_SMP */
#elif defined(_WRS_PPC_64BIT)
0x7c6000a6, /* 1 0x04 mfmsr p0 */
0x786300c0, /* 2 0x08 clrldi p0,p0,3 clear MSR[SF] */
0x7c600164, /* 3 0x0c mtmsrd p0 */
0x4c00012c, /* 4 0x10 isync */
#endif /* _EXC_OFF_CRTL, _WRS_PPC_64BIT */
/* If either of the above, add 4 words/0x10 bytes to following offsets */
0x7c6802a6, /* 1 0x04 mflr p0 */
0x48000001, /* 2(6) 0x08/18 bl xxxEnt */
0x38610000, /* 3 0x0c addi r3, sp, 0 */
0x9421fff0, /* 4 0x10 stwu sp, -FRAMEBASESZ(sp) */
0x48000001, /* 5(9) 0x14/24 bl xxxHandler */
0x38210010, /* 6 0x18 addi sp, sp, FRAMEBASESZ */
0x48000001 /* 7(11) 0x1c/2c bl xxxExit */
};
系统起来后在vxWorks shell 中输入d 0x500 (外部中断的内存地址),可以看到数据和excConnectCode存根一致
中断向量内存图分析:
P2020 BSP 中使用的中断向量内存分布图:
-------------
| 0x0 | vector base address
-------------
| 0x100 | Critical Input
-------------
| 0x200 | Machine Check
-------------
| 0x300 | Data Storage
-------------
| 0x400 | Instruction Storage
-------------
| 0x500 | External Input[0x500开始处存放excConnectCode存根]
| cVec[3] | 存放c函数地址高16位 cVec = 0x500 [3] = EXT_ENT_OFF
| cVec[4] | 存放entInt函数地址低16位
| cVec[8] | 存放&excHandlers[n]地址,其中excHandlers存放默认的ISR,该函数会读取IACK
| cVec[9] | [8] = EXT_ISR_OFF,存放ISR函数的低16位
| cVec[15] | 存放exitInt函数地址高16位 [15] = EXT_EXIT_OFF
| cVec[16] | 存放exitInt函数地址低16位
| ... |
-------------
| 0x600 | Alignment
-------------
| 0x700 | Program
-------------
| ... | /* execPpcLib.h 有定义 */
-------------
总的说来中断处理流程为:中断产生后,e500内核根据MSR,IVPR,IVORs等寄存器来确认异常类型,
以及该类中断向量地址,然后进行存根机器代码-->entInt(汇编)保存现场-->ISR(确认中断源,
处理的functions)-->exitInt(汇编)恢复现场,至于PC<-->SRR0 ,MSR<-->SRR1是在存根,
还是在entInt与exitInt中操作,有待细化...
6.总结
参考文献:
1.POWERPC(e500) 基于linux 中断过程分析
2.PowrPC基于vxWorks异常堆栈切换分析
3.PowerPC汇编指令以及通用/专用寄存器介绍
4.PowerPC OEA寄存器集介绍