我爱嵌入式系统

当前位置:首页 > 软件天地 > 业内资讯 > 详细内容
Arm linux 启动分析(1)
发布时间:2009/10/30  阅读次数:768  字体大小: 【】 【】【

Arm linux 启动分析(1)

                         ——bootloader及head_armv.S分析
  
王利明 walimi@peoplemail.com.cn
宋振宇zhenyusong@peoplemail.com.cn
2003-3-20

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

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
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//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+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:
                           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
/*
  * 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
  
/*
  * 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   //返回
--------------------------------------------------------------------------------------------------------------------------
  
我要评论
  • 匿名发表
  • [添加到收藏夹]
  • 发表评论:(匿名发表无需登录,已登录用户可直接发表。) 登录状态:未登录
最新评论
所有评论[0]
    暂无已审核评论!

51RTOS.com 版权所有  

Copyright 20006-2009 我爱嵌入式 ( 51RTOS.com ) All rights reserved 沪ICP备09080633号