Chinaunix首页 | 论坛 | 博客
  • 博客访问: 146378
  • 博文数量: 54
  • 博客积分: 2517
  • 博客等级: 少校
  • 技术积分: 540
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-13 18:52
文章分类
文章存档

2011年(2)

2010年(11)

2009年(41)

我的朋友

分类:

2009-09-17 21:58:56

1.概述:

在内核运行之前需要系统引导程序(Bootloader)完成加载内核和一些辅助性的工作,然后跳转到内核代码的起始地址并执行。本文先分析了Bootloader的初始化工作,接着从内核镜像的起始地址进行分析。整个arm linux内核的启动可分为三个阶段:第一阶段主要是进行cpu和体系结构的检查、cpu本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基础设施进行初始化;最后则是更高层次的初始化,如根设备和外部设备的初始化。

第一阶段的初始化是从内核入口(ENTRY(stext))开始到start_kernel前结束。这一阶段的代码在/arch/arm/head_armv.S中。

2Bootloader

2.1简介

       本处介绍主要来自内核源代码下的Documentation/arm/Booting文件,适合于arm linux 2.4.18-rmk6及以上版本。

       Bootloader主要作用是初始化一些必要的设备,然后调用内核,同时传递参数给内核。主要完成如下工作:

1.  建立和初始化RAM

2.  初始化一个串口。

3.  检测机器的系统结构。

4.  建立内核的tagged list

5.  调用内核镜像。

2.2功能详细介绍

       1.建立和初始化RAM

              要求:必须

              功能:探测所有的RAM位置和大小,并对RAM进行初始化。

       2.初始化一个串口。

              要求:可选,建议

功能:Bootloader应该初始化并启动一个串口。这可以让内核的串口驱动自动探测哪个串口作为内核的控制台。另外也可以通过给内核传递“console=”参数完成此工作。

       3.检测机器的系统结构。

              要求:必须

功能:Bootloader应该通过某种方法探测机器类型,最后传递给内核一个MACH_TYPE_xxx值,这些值参看linux/arch/arm/tools/mach-types

4.建立内核的tagged list

              要求:必须

功能:Bootloader必须创建和初始化内核的tagged list。一个合法的tagged list开始于ATAG_CORE 并结束于ATAG_NONEATAG_CORE tag可以为空。一个空的ATAG_CORE tagsize字段设为“2(0x00000002)ATAG_NONE size字段必须设为“0”。tagged list可以有任意多的tagBootloader必须至少传递系统内存的大小和位置,以及根文件系统的位置,一个最小化的tagged list应该像如下:

              +-----------+

base -> | ATAG_CORE |  |

        +-----------+  |

        | ATAG_MEM  |  | increasing address

        +-----------+  |

        | ATAG_NONE |  |

        +-----------+  v

tagged list应该放在内核解压时和initrd”bootp”程序都不会覆盖的内存区域。建议放在RAM的起始的16K大小的地方。

5.调用内核镜像。

              要求:必须

功能:可以从flash调用内核,也可以从系统RAM中调用内核。对于后者需要注意,内核使用内核镜像以下的16K内存作为页表,建议把内核起始放在RAM32K处。无论是哪种方法,如下条件必须满足:

- CPU register settings

  r0 = 0,

  r1 = machine type number discovered in (3) above.

  r2 = physical address of tagged list in system RAM.

- CPU mode

  All forms of interrupts must be disabled (IRQs and FIQs)

  The CPU must be in SVC mode.  (A special exception exists for Angel)

- Caches, MMUs

  The MMU must be off.

  Instruction cache may be on or off.

  Data cache must be off.

- The boot loader is expected to call the kernel image by jumping

  directly to the first instruction of the kernel image.

2.3 Skyeye相应说明

    因为Skyeye暂时没有Bootloader,所以以上一些设置必须由Skyeye在初始化的时候自己来完成,如r1的设置等。

3Head_armv.S分析

3.1 说明

    这个文件是arch/arm/kernel/head-armv.S,用汇编代码完成,是内核最先执行的一个文件。这一段汇编代码的主要作用,是检查cpu idarchitecture number,初始化页表、cpubbs等操作,并跳到start_kernel函数。它在执行前,处理器的状态应满足:

l        r0      - should be 0

l        r1      - unique architecture number

l        MMU     - off

l        I-cache - on or off

l        D-cache – off

3.2 流程

3.3 代码详细注释

    (略去一些条件编译的代码)

-------------------------------------------------------------------------------------------------------------------

/*

 * We place the page tables 16K below TEXTADDR.  Therefore, we must make sure

 * that TEXTADDR is correctly set.  Currently, we expect the least significant

 * "short" to be 0x8000, but we could probably relax this restriction to

 * TEXTADDR > PAGE_OFFSET + 0x4000

 *

 * Note that swapper_pg_dir is the virtual address of the page tables, and

 * pgtbl gives us a position-independent reference to these tables.  We can

 * do this because stext == TEXTADDR

 *

 * swapper_pg_dir, pgtbl and krnladr are all closely related.

 */

#if (TEXTADDR & 0xffff) != 0x8000

#error TEXTADDR must start at 0xXXXX8000

#endif

 

              .globl      SYMBOL_NAME(swapper_pg_dir)

              .equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000

 

              .macro    pgtbl, reg, rambase

              adr   \reg, stext

              sub  \reg, \reg, #0x4000

              .endm

 

/*

 * Since the page table is closely related to the kernel start address, we

 * can convert the page table base address to the base address of the section

 * containing both.

 */

              .macro    krnladr, rd, pgtable, rambase

              bic   \rd, \pgtable, #0x000ff000

              .endm

 

/*

 *  Kernel startup entry point.

 *

 * The rules are:

 *  r0      - should be 0

 *  r1      - unique architecture number

 *  MMU     - off

 *  I-cache - on or off

 *  D-cache - off

 *

 * See linux/arch/arm/tools/mach-types for the complete list of numbers

 * for r1.

 */

              .section ".text.init",#alloc,#execinstr

              .type       stext, #function

ENTRY(stext)  //内核入口点

              mov r12, r0  //r00r120

mov r0, #F_BIT | I_BIT | MODE_SVC@ make sure svc mode//程序状态,禁止FIQIRQ,设定Supervisor模式。0b11010011

              msr  cpsr_c, r0                     @ and all irqs disabled//置当前程序状态寄存器

              bl    __lookup_processor_type//跳转到判断cpu类型,查找运行的cpuid值,和

//linux编译支持的id值,是否有相等

              teq   r10, #0                         @ invalid processor?//没有则跳到__error

              moveq     r0, #'p'                   @ yes, error 'p'

              beq  __error

              bl    __lookup_architecture_type//跳转到判断体系类型,看r1寄存器的

//architecture number值是否支持。

              teq   r7, #0                           @ invalid architecture? //不支持,跳到出错

              moveq     r0, #'a'                   @ yes, error 'a'

              beq  __error

              bl    __create_page_tables//创建核心页表

              adr   lr, __ret                 @ return address//lr=0xc0028054

              add  pc, r10, #12      @ initialise processor//r10pointer to processor structure

                                                                             //__arm720_proc_info

                                                                             //r1012__arm720_setup;见

//__arm720_proc_infoproc-arm720.S

__arm720_setup: mov  r0, #0

                   mcr   p15, 0, r0, c7, c7, 0            @ invalidate caches

                   mcr   p15, 0, r0, c8, c7, 0            @ flush TLB (v4)

                   mcr   p15, 0, r4, c2, c0                 @ load page table pointer

//cp15寄存器1(ttb)0xc0024000

                   mov  r0, #0x1f                     @ Domains 0, 1 = client

                   mcr   p15, 0, r0, c3, c0                 @ load domain access register

 

                   mrc   p15, 0, r0, c1, c0                 @ get control register//r00x70

                   bic    r0, r0, #0x0e00                            @ ..V. ..RS BLDP WCAM//bit[11:9]=0

                                                                                                                                   r00x00000070

                   orr    r0, r0, #0x2100                            @ .... .... .111 .... (old) //r00x00002170

                   orr    r0, r0, #0x003d                            @ ..1. ..01 ..11 1101 (new) //r00x0000217d

其中S  LDPWC  M位置1

(详见cp15寄存器1说明)

                                                                                                                        

                   mov  pc, lr                                     @ __ret (head-armv.S)

                                                 @ (return control reg)

.type       __switch_data, %object

__switch_data:       .long       __mmap_switched

              .long       SYMBOL_NAME(__bss_start)

              .long       SYMBOL_NAME(_end)

              .long       SYMBOL_NAME(processor_id)

              .long       SYMBOL_NAME(__machine_arch_type)

              .long       SYMBOL_NAME(cr_alignment)

              .long       SYMBOL_NAME(init_task_union)+8192

 

              .type       __ret, %function

__ret:             ldr   lr, __switch_data

              mcr  p15, 0, r0, c1, c0//更新控制寄存器cp15寄存器10x0000217d

 

              mov r0, r0

              mov r0, r0

              mov r0, r0

              mov pc, lr//__switch_data

 

              /*

               * This code follows on after the page

               * table switch and jump above.

               *

               * r0  = processor control register

               * r1  = machine ID

               * r9  = processor ID

               */

              .align      5

__mmap_switched://sp指针指向init_task_union+8192include/linux/sched.h)处,即第

//一个进程的task_struct和系统堆栈的地址;清空BSS段;保存processor ID

//machine type,到全局变量processor_id__machine_arch_type,这些值

//以后要用到;r0"A"置位的control register 值,r2"A"清空的

//control register 值,即对齐检查(Alignment fault checking)位,并保

//存到cr_alignment,和cr_no_alignment(在文件entry-armv.S中)。最

//后跳转到start_kernelinit/main.c

              adr   r3, __switch_data + 4//__bss_start

              ldmia      r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat//r20xc0000000

                                                 @ sp = stack pointer

//r40xc00c04e0__bss_start

//r50xc00e02a8_end

//r60xc00c0934processor_id

//r70xc00c0930__machine_arch_type

//r80xc00bcb88cr_alignment

//sp0xc00bc000;(init_task_union)+8192

 

              mov fp, #0                           @ Clear BSS (and zero fp)

1:           cmp r4, r5

              strcc fp, [r4],#4

              bcc  1b

 

              str    r9, [r6]                  @ Save processor ID

              str    r1, [r7]                  @ Save machine type

#ifdef CONFIG_ALIGNMENT_TRAP

              orr   r0, r0, #2               @ ...........A.

#endif

bic   r2, r0, #2               @ Clear 'A' bit//r0=0x217d

              stmia       r8, {r0, r2}                   @ Save control register values

              b     SYMBOL_NAME(start_kernel)//跳转到start_kernel

/*

 * Setup the initial page tables.  We only setup the barest

 * amount which are required to get the kernel running, which

 * generally means mapping in the kernel code.

 *

 * We only map in 4MB of RAM, which should be sufficient in

 * all cases.

 *

 * r5 = physical address of start of RAM

 * r6 = physical IO address

 * r7 = byte offset into page tables for IO

 * r8 = page table flags

 */

__create_page_tables:

              pgtbl       r4, r5             @ page table address//调用宏pgtblr40xc0024000:页表

基址

 

              /*

               * Clear the 16K level 1 swapper page table

               */

              mov r0, r4//r00xc0024000

              mov r3, #0

              add  r2, r0, #0x4000//r2=0xc0028000

1:           str    r3, [r0], #4

              str    r3, [r0], #4

              str    r3, [r0], #4

              str    r3, [r0], #4

              teq   r0, r2   

              bne  1b   //将地址0xc00240000xc00280000

 

              /*

               * Create identity mapping for first MB of kernel to

               * cater for the MMU enable.  This identity mapping

               * will be removed by paging_init()

               */

              krnladr    r2, r4, r5               @ start of kernel//r20xc0000000r40xc0024000

              add  r3, r8, r2               @ flags + kernel base//flagsr80xc1er30xc0000c1e

              str    r3, [r4, r2, lsr #18] @ identity mapping//addr0xc0027000;value0xc0000c1e

 

              /*

               * Now setup the pagetables for our kernel direct

               * mapped region.  We round TEXTADDR down to the

               * nearest megabyte boundary.

               */

              add  r0, r4, #(TEXTADDR & 0xff000000) >> 18 @ start of kernel//r00xc0027000

              bic   r2, r3, #0x00f00000 //r20xc0000c1e

              str    r2, [r0]                  @ PAGE_OFFSET + 0MB

              add  r0, r0, #(TEXTADDR & 0x00f00000) >> 18

              str    r3, [r0], #4                   @ KERNEL + 0MB

              add  r3, r3, #1 << 20

              str    r3, [r0], #4                   @ KERNEL + 1MB

              add  r3, r3, #1 << 20

              str    r3, [r0], #4                   @ KERNEL + 2MB

              add  r3, r3, #1 << 20

              str    r3, [r0], #4                   @ KERNEL + 3MB

//核心页表:

//addr                          一级描述符值

//0xc0027000              0xc0000c1e

//0xc0027004              0xc0100c1e

//0xc0027008           0xc0200c1e

//0xc002700c                  0xc0300c1e     r00xc0027010

              /*

               * Ensure that the first section of RAM is present.

               * we assume that:

               *  1. the RAM is aligned to a 32MB boundary

               *  2. the kernel is executing in the same 32MB chunk

               *     as the start of RAM.

               */

              bic   r0, r0, #0x01f00000 >> 18    @ round down//r00xc0027000

              and  r2, r5, #0xfe000000              @ round down//r20xc0000000

              add  r3, r8, r2               @ flags + rambase//r30xc0000c1e

              str    r3, [r0]

 

              bic   r8, r8, #0x0c                 @ turn off cacheable//r80xc12

                                                 @ and bufferable bits

              mov pc, lr

/*

 * Read processor ID register (CP#15, CR0), and look up in the linker-built

 * supported processor list.  Note that we can't use the absolute addresses

 * for the __proc_info lists since we aren't running with the MMU on

 * (and therefore, we are not in the correct address space).  We have to

 * calculate the offset.

 *

 * Returns:

 *    r5, r6, r7 corrupted

 *    r8  = page table flags

 *    r9  = processor ID

 *    r10 = pointer to processor structure

 */

__lookup_processor_type://判断cpu类型

              adr   r5, 2f  //取标号2的地址

              ldmia      r5, {r7, r9, r10} //r7__proc_info_endr9__proc_info_beginr10r5

              sub  r5, r5, r10                    @ convert addresses  //r5=0??

              add  r7, r7, r5               @ to our address space

              add  r10, r9, r5  //r10__proc_info_begin

mrc p15, 0, r9, c0, c0   @ get processor id  //读取cp15寄存器0cpu idr9。在

此版本是0x41807200

1:           ldmia      r10, {r5, r6, r8}     @ value, mask, mmuflags  //读取arm linuxcpu信息

                                               // r5id0x41807200r6mask

//0xffffff00r8mmuflags即一级描

//述符:0xc1e

              and  r6, r6, r9               @ mask wanted bits  //屏蔽cpu id的低8

              teq   r5, r6  //寄存器0cpu idarm linuxcpu id比较

              moveq     pc, lr  //相同则返回

              add  r10, r10, #36                @ sizeof(proc_info_list)//否则寻找下一块proc_info

              cmp r10, r7

              blt   1b

              mov r10, #0                         @ unknown processor //没有匹配信息,r100

              mov pc, lr

 

/*

 * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for

 * more information about the __proc_info and __arch_info structures.

 */

2:           .long       __proc_info_end

              .long       __proc_info_begin

              .long       2b

              .long       __arch_info_begin

              .long       __arch_info_end

 

/*

 * Lookup machine architecture in the linker-build list of architectures.

 * Note that we can't use the absolute addresses for the __arch_info

 * lists since we aren't running with the MMU on (and therefore, we are

 * not in the correct address space).  We have to calculate the offset.

 *

 *  r1 = machine architecture number

 * Returns:

 *  r2, r3, r4 corrupted

 *  r5 = physical start address of RAM

 *  r6 = physical address of IO

 *  r7 = byte offset into page tables for IO

 */

__lookup_architecture_type://判断体系类型

              adr   r4, 2b//取上面标号2的地址

              ldmia      r4, {r2, r3, r5, r6, r7}    @ throw away r2, r3//r5:r4r6__arch_info_begin

r7__arch_info_end

              sub  r5, r4, r5               @ convert addresses//r5=0

              add  r4, r6, r5               @ to our address space//r4__arch_info_begin

              add  r7, r7, r5

1:           ldr   r5, [r4]                  @ get machine type

              teq   r5, r1//r1machine type 号,此为91

              beq  2f

              add  r4, r4, #SIZEOF_MACHINE_DESC//不匹配,查找下一个arch_info

              cmp r4, r7

              blt   1b

              mov r7, #0                           @ unknown architecture

              mov pc, lr

2:           ldmib      r4, {r5, r6, r7}              @ found, get results//r5ram物理起始地址:

0xc0000000r6io地址:0x8000000

r7io在页表的偏移:0x3fc0

              mov pc, lr  //返回

阅读(1200) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~