Board logo

标题: Arm linux 启动分析 [打印本页]

作者: linuxarm    时间: 2006-12-21 12:32     标题: Arm linux 启动分析

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

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

2.Bootloader
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。


作者: linuxarm    时间: 2006-12-21 12:32

4.建立内核的tagged list。

        要求:必须

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

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

base -> | ATAG_CORE |  |

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

    | ATAG_MEM  |  | increasing address

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

    | ATAG_NONE |  |

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

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

5.调用内核镜像。

        要求:必须

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

- 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的设置等。


作者: linuxarm    时间: 2006-12-21 12:32

3.Head_armv.S分析
3.1 说明
    这个文件是arch/arm/kernel/head-armv.S,用汇编代码完成,是内核最先执行的一个文件。这一段汇编代码的主要作用,是检查cpu id,architecture number,初始化页表、cpu、bbs等操作,并跳到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


作者: linuxarm    时间: 2006-12-21 12:32

ENTRY(stext)  //内核入口点

        mov r12, r0  //r0=0,r12=0

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

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

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

//此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//r10:pointer to processor structure

                               //(__arm720_proc_info)

                                  //r10+12:__arm720_setup;见

//__arm720_proc_info(proc-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//r0=0x70

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

                                            r0=0x00000070

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

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

其中S  LDPWC  M位置1。

(详见cp15寄存器1说明)


作者: linuxarm    时间: 2006-12-21 12:33

__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寄存器1=0x0000217d

 

        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+8192(include/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_kernel(init/main.c)

        adr r3, __switch_data + 4//__bss_start

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

                         @ sp = stack pointer

//r4=0xc00c04e0;__bss_start

//r5=0xc00e02a8;_end

//r6=0xc00c0934;processor_id

//r7=0xc00c0930;__machine_arch_type

//r8=0xc00bcb88;cr_alignment

//sp=0xc00bc000;(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:


作者: linuxarm    时间: 2006-12-21 12:34

      pgtbl   r4, r5      @ page table address//调用宏pgtbl,r4=0xc0024000:页表

基址

 

        /*

        * Clear the 16K level 1 swapper page table

        */

       mov r0, r4//r0=0xc0024000

       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   //将地址0xc0024000~0xc0028000清0

 

        /*

        * 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//r2=0xc0000000;r4=0xc0024000

       add r3, r8, r2          @ flags + kernel base//flags,r8=0xc1e;r3=0xc0000c1e

       str r3, [r4, r2, lsr #18]   @ identity mapping//addr:0xc0027000;value:0xc0000c1e

 

        /*

        * 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//r0=0xc0027000

       bic r2, r3, #0x00f00000 //r2=0xc0000c1e

       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     r0=0xc0027010

       /*

        * 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//r0=0xc0027000

       and r2, r5, #0xfe000000     @ round down//r2=0xc0000000

       add r3, r8, r2          @ flags + rambase//r3=0xc0000c1e

       str r3, [r0]

 

        bic r8, r8, #0x0c           @ turn off cacheable//r8=0xc12

                         @ and bufferable bits

       mov pc, lr


作者: linuxarm    时间: 2006-12-21 12:34

/*

 * 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_end;r9:__proc_info_begin;r10:r5

       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寄存器0中cpu id至r9。在

此版本是0x41807200

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

                                               // r5,id:0x41807200;r6,mask:

//0xffffff00;r8,mmuflags即一级描

//述符:0xc1e

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

        teq r5, r6  //寄存器0的cpu id与arm linux中cpu id比较

        moveq   pc, lr  //相同则返回

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

       cmp r10, r7

       blt 1b

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

       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

 


作者: linuxarm    时间: 2006-12-21 12:34

/*

 * 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:r4;r6:__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//r1是machine 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//r5,ram物理起始地址:

0xc0000000;r6,io地址:0x8000000;

r7,io在页表的偏移:0x3fc0

       mov pc, lr  //返回






欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0