建立虚拟地址到物理地址的映射
/********************************************************************/
while ((virt & 0xfffff || (virt + off) & 0xfffff) && length >= PAGE_SIZE) {
alloc_init_page(virt, virt + off, md->domain, prot_pte);
virt += PAGE_SIZE;
length -= PAGE_SIZE;
}
while (length >= PGDIR_SIZE) {
alloc_init_section(virt, virt + off, prot_sect);
virt += PGDIR_SIZE;
length -= PGDIR_SIZE;
}
while (length >= PAGE_SIZE) {
alloc_init_page(virt, virt + off, md->domain, prot_pte);
virt += PAGE_SIZE;
length -= PAGE_SIZE;
}
/*************************************************************************/
create_mapping的作用是设置虚地址virt 到物理地址virt + off_set的映射页目录和页表。
/*************************************************************************/
/* 映射中断向量表区域 */
init_maps->physical = virt_to_phys(init_maps);
init_maps->virtual = vectors_base();
init_maps->length = PAGE_SIZE;
init_maps->domain = DOMAIN_USER;
init_maps->prot_read = 0;
init_maps->prot_write = 0;
init_maps->cacheable = 1;
init_maps->bufferable = 0;
create_mapping(init_maps);
中断向量表的虚地址init_maps,是用alloc_bootmem_low_pages分配的,通常是在PAGE_OFF+0x8000前面的某一页,vectors_base()是个宏,ARM规定中断向量表的地址只能是0或0xFFFF0000,所以上述代码映射一页到0或0xFFFF0000,中断处理程序中的部分代码也被拷贝到这一页中。
5.3 parse_options()
分析由内核引导程序发送给内核的启动选项,在初始化过程中按照某些选项运行,并将剩余部分传送给init进程。这些选项可能已经存储在配置文件中,也可能是由用户在系统启动时敲入的。但内核并不关心这些,这些细节都是内核引导程序关注的内容,嵌入式系统更是如此。
5.4 trap_init()
这个函数用来做体系相关的中断处理的初始化,在该函数中调用__trap_init((void*)vectors_base())函数将exceptionvector设置到vectors_base开始的地址上。__trap_init函数位于entry-armv.S文件中,对于ARM处理器,共有复位、未定义指令、SWI、预取终止、数据终止、IRQ和FIQ几种方式。SWI主要用来实现系统调用,而产生了IRQ之后,通过exceptionvector进入中断处理过程,执行do_IRQ函数。
armnommu的trap_init()函数在arch/armnommu/kernel/traps.c文件中。vectors_base是写中断向量的开始地址,在include/asm-armnommu/proc-armv/system.h文件中设置,地址为0或0XFFFF0000。
ENTRY(__trap_init)
stmfd sp!, {r4 - r6, lr}
mrs r1, cpsr @ code from 2.0.38
bic r1, r1, #MODE_MASK @ clear mode bits /* 设置svc模式,disable IRQ,FIQ */
orr r1, r1, #I_BIT|F_BIT|MODE_SVC @ set SVC mode, disable IRQ,FIQ
msr cpsr, r1
adr r1, .LCvectors @ set up the vectors
ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr} /* 拷贝异常向量 */
add r2, r0, #0x200
adr r0, __stubs_start @ copy stubs to 0x200
adr r1, __stubs_end
1: ldr r3, [r0], #4
str r3, [r2], #4
cmp r0, r1
blt 1b
LOADREGS(fd, sp!, {r4 - r6, pc})
__stubs_start到__stubs_end的地址中包含了异常处理的代码,因此拷贝到vectors_base+0x200的位置上。
5.5 init_IRQ()
void __init init_IRQ(void)
{
extern void init_dma(void);
int irq;
for (irq = 0; irq < NR_IRQS; irq++) {
irq_desc[irq].probe_ok = 0;
irq_desc[irq].valid = 0;
irq_desc[irq].noautoenable = 0;
irq_desc[irq].mask_ack = dummy_mask_unmask_irq;
irq_desc[irq].mask = dummy_mask_unmask_irq;
irq_desc[irq].unmask = dummy_mask_unmask_irq;
}
CSR_WRITE(AIC_MDCR, 0x7FFFE); /* disable all interrupts */
CSR_WRITE(CAHCNF,0x0);/*Close Cache*/
CSR_WRITE(CAHCON,0x87);/*Flush Cache*/
while(CSR_READ(CAHCON)!=0);
CSR_WRITE(CAHCNF,0x7);/*Open Cache*/
init_arch_irq();
init_dma();
}
这个函数用来做体系相关的irq处理的初始化,irq_desc数组是用来描述IRQ的请求队列,每一个中断号分配一个irq_desc结构,组成了一个数组。NR_IRQS代表中断数目,这里只是对中断结构irq_desc进行了初始化。在默认的初始化完成后调用初始化函数init_arch_irq,先执行arch/armnommu/kernel/irq-arch.c文件中的函数genarch_init_irq(),然后就执行include/asm-armnommu/arch-xxxx/irq.h中的inline函数irq_init_irq,在这里对irq_desc进行了实质的初始化。其中mask用阻塞中断;unmask用来取消阻塞;mask_ack的作用是阻塞中断,同时还回应ack给硬件表示这个中断已经被处理了,否则硬件将再次发生同一个中断。这里,不是所有硬件需要这个ack回应,所以很多时候mask_ack与mask用的是同一个函数。
接下来执行init_dma()函数,如果不支持DMA,可以设置include/asm-armnommu/arch-xxxx/dma.h中的MAX_DMA_CHANNELS为0,这样在arch/armnommu/kernel/dma.c文件中会根据这个定义使用不同的函数。
5.6 sched_init()
初始化系统调度进程,主要对定时器机制和时钟中断的BottomHalf的初始化函数进行设置。与时间相关的初始化过程主要有两步:(1)调用init_timervecs()函数初始化内核定时器机制;(2)调用init_bh()函数将BH向量TIMER_BH、TQUEUE_BH和IMMEDIATE_BH所对应的BH函数分别设置成timer_bh()、tqueue_bh()和immediate_bh()函数
5.7 softirq_init()
内核的软中断机制初始化函数。调用tasklet_init初始化tasklet_struct结构,软中断的个数为32个。用于bh的tasklet_struct结构调用tasklet_init()以后,它们的函数指针func全都指向bh_action()。bh_action就是tasklet实现bh机制的代码,但此时具体的bh函数还没有指定。
HI_SOFTIRQ用于实现bottom half,TASKLET_SOFTIRQ用于公共的tasklet。
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); /* 初始化公共的tasklet_struct要用到的软中断 */
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); /* 初始化tasklet_struct实现的bottom half调用 */
这里顺便讲一下软中断的执行函数do_softirq()。
软中断服务不允许在一个硬中断服务程序内部执行,也不允许在一个软中断服务程序内部执行,所以通过in_interrupt()加以检查。h->action 就是串行化执行软中断,当bh 的tasklet_struct链入的时候,就能在这里执行,在bh里重新锁定了所有CPU,导致一个时间只有一个CPU可以执行bh函数,但是do_softirq()是可以在多CPU上同时执行的。而每个tasklet_struct在一个时间上是不会出现在两个CPU上的。另外,只有当Linux初始化完成开启中断后,中断系统才可以开始工作。
5.8 time_init()
这个函数用来做体系相关的timer的初始化,armnommu的在arch/armnommu/kernel/time.c。这里调用了在include/asm-armnommu/arch-xxxx/time.h中的inline函数setup_timer,setup_timer()函数的设计与硬件设计紧密相关,主要是根据硬件设计情况设置时钟中断号和时钟频率等。
void __inline__ setup_timer (void)
{
/*----- disable timer -----*/
CSR_WRITE(TCR0, xxx);
CSR_WRITE (AIC_SCR7, xxx); /* setting priority level to high */
/* timer 0: 100 ticks/sec */
CSR_WRITE(TICR0, xxx);
timer_irq.handler = xxxxxx_timer_interrupt;
setup_arm_irq(IRQ_TIMER, &timer_irq); /* IRQ_TIMER is the interrupt number */
INT_ENABLE(IRQ_TIMER);
/* Clear interrupt flag */
CSR_WRITE(TISR, xxx);
/* enable timer */
CSR_WRITE(TCR0, xxx);
}
5.9 console_init()
控制台初始化。控制台也是一种驱动程序,由于其特殊性,提前到该处完成初始化,主要是为了提前看到输出信息,据此判断内核运行情况。很多嵌入式Linux操作系统由于没有在/dev目录下正确配置console设备,造成启动时发生诸如unable to open an initialconsole的错误。
/*******************************************************************************/
init_modules()函数到smp_init()函数之间的代码一般不需要作修改,
如果平台具有特殊性,也只需对相关函数进行必要修改。
这里简单注明了一下各个函数的功能,以便了解。
/*******************************************************************************/
5.10 init_modules()
模块初始化。如果编译内核时使能该选项,则内核支持模块化加载/卸载功能
5.11 kmem_cache_init()
内核Cache初始化。
5.12 sti()
使能中断,这里开始,中断系统开始正常工作。
[ 本帖最后由 jufeng2309 于 2007-3-25 15:10 编辑 ]
阅读(939) | 评论(0) | 转发(0) |