看连接脚本,异常向量连接在开始的 0x100 地址(空位填充0,ALIGN 控制)
如果 RTEMS 是放到SDRAM中运行,0x30000000 - 0x30000100 就应该是异常向量。
然后 0x30000100 开始的是 _start 这个entry,代码从这里开始。
从start.S 的开始部分没有看到常见的向量表。因为RTEMS期望有一个bootloader将它
本身复制到SDRAM中并且运行。然后RTEMS自己创建动态的向量表(在RAM中的向量表)
当然了,其实可以使用自己写的代码,实现.text .data 段的复制等。
而ARM的异常向量固定在 0x0 开始的地址,所以start.S 中使能了 MMU,将 0x30000000
地址映射到 0 地址。
ARM发生异常时,经过MMU的映射,实际上执行的是 0x30000000 开始的向量表中的操作。
.base :
{
arm_exception_table = .;
arm_reset_vect = .; /* 0x00 */
. += 4;
arm_undef_vect = .; /* 0x04 */
. += 4;
arm_swi_vect = .; /* 0x08 */
. += 4;
arm_iabrt_vect = .; /* 0x0c */
. += 4;
arm_dabrt_vect = .; /* 0x10 */
. += 4;
/* no vector here */
. += 4;
arm_irq_vect = .; /* 0x18 */
. += 4;
arm_fiq_vect = .; /* 0x1c */
. += 4;
/* FIXME: */
rtems_vector_table = .;
. += (8 * 4); /* 8 ARM interrupts */
bsp_vector_table = .;
. += (32 * 4); /* 32 S3C2400 interrupts */
. = ALIGN (0x100);
接着 start.S 中看到如下代码,很显然这里是安装句柄。但是问题来了,为什么这些句柄都是
简单的死循环?实际的操作是怎么样的呢?这个问题我确实思考了好几天。后来明白了,这里仅仅
是占个茅坑。做好基本的向量捕捉,至少让系统不能死,发生异常能捕捉到。至于怎么处理这些异常,
可以通过后面注册的处理句柄去做处理。
mov r0, #0
adr r1, vector_block
ldmia r1!, {r2-r9}
stmia r0!, {r2-r9}
ldmia r1!, {r2-r9}
stmia r0!, {r2-r9}
vector_block:
ldr pc, Reset_Handler
ldr pc, Undefined_Handler
ldr pc, SWI_Handler
ldr pc, Prefetch_Handler
ldr pc, Abort_Handler
nop
ldr pc, IRQ_Handler
ldr pc, FIQ_Handler
Reset_Handler: b bsp_reset
Undefined_Handler: b Undefined_Handler
SWI_Handler: b SWI_Handler
Prefetch_Handler: b Prefetch_Handler
Abort_Handler: b Abort_Handler
nop
IRQ_Handler: b IRQ_Handler
FIQ_Handler: b FIQ_Handler
比如常用的 IRQ 中断,系统是怎么做的呢?
虽然说写文章一般用顺序,但是我打算我分析的时候的思路写下来,看看怎么思考这个问题的。
首先看 RTEMS 的例子和doc文档,应该不难发现RTEMS有这么一个函数
rtems_status_code rtems_interrupt_catch(
rtems_isr_entry new_isr_handler,
rtems_vector_number vector,
rtems_isr_entry *old_isr_handler
)
catch就是捕捉的意思,看这里的参数,应该就是注册一个新的中断处理句柄,然后返回旧的句柄。
至于 vector 这里不知道含义是什么,一般的推断应该就是中断号,比如s3c2440 有几个异常向量
IRQ中断也有很多个不同的中断源,这里的vector到底是指什么?这里其实并不知道的。
接着就应该想,这个函数到底被谁调用,或者这个函数调用谁?这就是思路。
rtems_interrupt_catch 已经是用户可以调用的函数了。(其实我是在看网络驱动代码的时候发现的)
#define _ISR_Install_vector( _vector, _new_handler, _old_handler ) \
_CPU_ISR_install_vector( _vector, _new_handler, _old_handler )
最后发现
if (current_handler != (uint32_t) new_handler) {
table [vector] = (uint32_t) new_handler;
}
所谓的安装句柄,就是将句柄(也就是函数了)加入到 table 数组当中。
volatile uint32_t *table = (volatile uint32_t *) (MAX_EXCEPTIONS * 4);
这里可以看出table是指向一个绝对地址,MAX_EXCEPTIONS 是 8,每个异常向量是 4byte
很自然就想到,这个table 就是跟在所有的异常向量的后面。
看会 link脚本,应该就是指向这个地址了。
rtems_vector_table = .;
. += (8 * 4); /* 8 ARM interrupts */
bsp_vector_table = .;
. += (32 * 4); /* 32 S3C2400 interrupts */
可见,这是rtems自己的向量表,在看看哪里有使用这个标号,在 s3c2410.h 中定义了
#define _ISR_STARTADDRESS rtems_vector_table
/* ISR */
#define pISR_RESET (*(unsigned *)(_ISR_STARTADDRESS+0x0))
#define pISR_UNDEF (*(unsigned *)(_ISR_STARTADDRESS+0x4))
看 bootcard.c
bsp_start();
void bsp_start_default( void )
(应为属性是 weak 函数,所以被替换掉了)
void bsp_start (void) __attribute__ ((weak, alias("bsp_start_default")));字面意思猜
在这个bsp的初始化函数里面,(注意这个函数很早被执行,因为需要安装异常句柄。
rtems_exception_init_mngt();
_CPU_ISR_install_vector(ARM_EXCEPTION_IRQ,
_Exception_Handler_Abort,
NULL);
安装的函数使用之前的函数,先指向 abort处理句柄,然后执行下面的
bsp_interrupt_initialize 中断初始化。
接着执行
bsp_interrupt_facility_initialize();
实际上就是安装IRQ处理句柄为 arm_exc_interrupt
_CPU_ISR_install_vector(ARM_EXCEPTION_IRQ, arm_exc_interrupt, NULL);
这个函数在 arm_exc_interrupt.S 中,是汇编写的。
因为还有内核调度等功能,所以这个函数看起来有点复杂,但是我们留意有下面一句
bl bsp_interrupt_dispatch
void bsp_interrupt_dispatch(void)
{
rtems_vector_number vector = *((uint32_t *) rINTOFFSET_ADDR);
bsp_interrupt_handler_dispatch(vector);
}
将 INTOFFSET 的内容作为 vector 的参数,传递给bsp_interrupt_handler_dispatch
然后根据 bsp_interrupt_handler_table 的内容(之前或者用户自己安装的句柄),执行具体的函数。
当分析到这里的时候,上层的基本原理都明白了。但是始终还是不明白,RTEMS 的中断句柄表是 (MAX_EXCEPTIONS * 4) 的偏移
到底为什么能将ARM的IRQ中断转为RTEMS的中断的??
想了整整一个中午,没睡觉,下午在google看了篇文章突然间明白了。
rtems_exception_init_mngt 安装的是在 (MAX_EXCEPTIONS * 4) 偏移处,也就是 0x20
这个时候看看 start.S 里面安装的处理句柄,不就是这个地址么!!!!!
vector_block:
ldr pc, Reset_Handler
ldr pc, Undefined_Handler
于是一切问题都解决了。
Etual
2012-12-21 (对的,末日)