- UID
- 1029342
- 性别
- 男
|
第三章、ARM Linux
上述所说,bootloader会跳到压缩的内核映像代码,并传递一些ATAG标记的初始化参数,压缩内核是以‘start’标签开始,这个标签定义在arch/arm/boot/compressed/head.s 汇编文件里。从这一步开始,引导过程包含3个阶段。 一、内核首先解压自己; 二、处理器依赖部分的内核代码执行:主要初始化CPU和内存; 三、最后,独立于处理器部分的内核代码执行:即开始ARM多核处理,通过启动所有ARM11的核,并且初始化所有内核组件和数据结构; 下图是ARMLinux内核的引导概图:
启动分三步:1、映像解压:a、U-Boot跳到“start”标签,标签在 /arm/boot/compressed/head.S文件里;b、参数通过U-Boot r0保存(CPU架构ID)和r1(ATAG参数列表指针)来传递;c、执行cpu架构相关代码,然后关闭缓存和MMU;d、正确C环境设置;e、分配适当的值给寄存器和堆栈指针。如 r4 = 内核物理起始地址 - sp = 解压器代码地址;f、再一次把cache memory打开,cache memory例程遍历proc_type 列表,找出对应的arm 架构类型, 对ARM11 多核(ARM v6): __armv4_mmu_cache_on 打开 __armv4_mmu_cache_off 关闭 __armv6_mmu_cache_flush 刷新缓存内存到内存g、确定将要解压的内核映像是否会覆盖压缩的内核映像,并跳到相应的处理例程;h、调用解压例程:decompress_kernel(),位置在arch/arm/boot/compressed/misc.c 这个函数会在输出终端上显示“Uncompressing Linux...“消息; 接着调用gunzip()函数,并显示“done, booting the kernel” 消息;i、调用__armv6_mmu_cache_flush函数刷新cache 内存的内容到RAM;j、调用__armv4_mmu_cache_off关闭,因为内核初始化例程希望cache在开始的时候是关闭的;k、跳到内存中内核的开始地址,这个地址保存在r4寄存器中;l、内核的起始地址依据不同的平台架构而不同,对PB11MPCore核,保存在arch/arm/mach-realview/Makefile.boot文件里的变量zreladdr-y中, zreladdr-y := 0x000080002、处理器(ARM)依赖的内核代码 内核开始入口点定义在文本文件:arch/arm/kernel/head.S中,解压器关闭MMU、cache内存、设置好合适的寄存器值后跳到这个入口。共包含以下事件序列: a、确保CPU运行在超级模式并禁用所有中断; b、调用 __lookup_processor_type(arch/arm/kernel/head-common.S)查找处理器类型,该函数返回一个指向proc_info_list(变量定义在 arch/arm/mm/proc-v6.S)的指针,这一项是ARM11对应的处理器信息; c、调用 __lookup_machine_type(arch/arm/kernel/head-common.S)查找机器类型,该函数返回一个指向 machine_desc 结构的指针,这一项 专门为 PB11MPCore 核定义的; d、 调用 __create_page_tables建立页表,个数是内核运行所需要的数量;也就是说在内核执行过程中映射用; e、跳到__v6_setup procedure例程,(arch/arm/mm/proc-v6.S),这个例程初始化CPU0的TLB,cache,MMU; f、使能MMU,函数是__enable_mmu(),它设置一些配置位后调用函数__turn_mmu_on()(arch/arm/kernel/head.S); g、在函数__turn_mmu_on中,会设置合适的控制寄存器值,然后跳到__switch_data,执行第一个函数__mmap_switched() (在arch/arm/kernel/head-common.S文件中); h、在__mmap_switched()中,copy到RAM的数据段和BSS段被清0,最后跳到start_kernel()例程,该函数在init/main.c,这个是LINUX的开始处。3、处理器(ARM)无关的内核代码 从这个阶段开始,就是公共的处理序列,是独立于硬件架构的Linux内核引导过程;但仍有一些函数依赖于硬件,并且会覆盖独立于硬件的代码的执行。我们会专注于多核Linux部分的启动和cpus的初始化,主要针对ARM11的多核架构而言。 第一步、函数 start_kernel(): (init/main.c) <目前我们在处理器0> a、用local_irq_disable()函数屏蔽CPU0上的中断 (include/linux/irqflags.h); b、用lock_kernel()函数锁内核,避免被高优先级中断抢占(include/linux/smp-lock.h); c、用函数boot_cpu_init() (init/main.c)激活CPU0; d、用函数tick_init() (kernel/time/tick-common.c)初始化内核tick控制器; e、用函数page_address_init() (mm/highmem.c)初始化内存子系统; f、用函数printk(linux_banner) (init/version.c)打印内核版本信息到终端; g、用函数setup_arch(&command_line)设置架构特有的子系统如内存、I/O、处理器、等等,其中command_line 是从U-Boot传来的参数列表 (arch/arm/kernel/setup.c); 1)在函数setup_arch(&command_line) 中, 我们执行架构相关的代码。对ARM11 多核, 是调用smp_init_cpus()函数,这个函数初始化cpu的映射。 就是在这一阶段,内核知道在ARM11系统架构里,有4个核。(arch/arm/mach-realview/platsmp.c) 2)用函数cpu_init()初始化一个处理器(这一步是指CPU0 ),它复制cache信息,初始化多核相关的信息,并设置每一个CPU的栈(arch/arm/kernel/setup.c); |
|