内核版本:2.6.22  为什么要采用这样一个较低的版本进行移植了,因为韦东山大牛说了,低版本的才能学到东西,越是高版本需要移植时做的工作量越少,学的东西越少。

内核启动分为三个阶段,第一是运行head.S文件和head-common.S,第三个阶段是允许第二是运行main.c文件

对于ARM的处理器,内核第一个启动的文件是arc/arm/kernel下面的head.S文件。当然arc/arm/boot/compress下面也有这个文件,这个文件和上面的文件略有不同,当要生成压缩的内核时zImage时,启动的是后者,后者与前者不同的时,它前面的代码是做自解压的,后面的代码都相同。我们这里这分析arc/arm/kernel下面的head.S文件。当head.S所作的工作完成后它会跳到init/目录下跌的main.c的start_kernel函数开始执行。

第一阶段:

首先截取部分head.S文件

ENTRY(stext)

msr  cpsr_c,#PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode

@ andirqs disabled

mrc  p15,0, r9, c0, c0           @ get processor id

bl    __lookup_processor_type             @ r5=procinfo r9=cpuid

movs       r10,r5                         @ invalidprocessor (r5=0)?

beq  __error_p                     @ yes, error 'p'

bl    __lookup_machine_type        @ r5=machinfo

movs       r8,r5                           @ invalidmachine (r5=0)?

beq  __error_a                     @ yes, error 'a'

bl    __create_page_tables

/*

*The following calls CPU specific code in a position independent

*manner.  See arch/arm/mm/proc-*.S fordetails.  r10 = base of

*xxx_proc_info structure selected by __lookup_machine_type

*above.  On return, the CPU will be readyfor the MMU to be

*turned on, and r0 will hold the CPU control register value.

*/

ldr   r13,__switch_data        @ address to jump toafter

@ mmuhas been enabled

adr   lr,__enable_mmu          @ return (PIC)address

第一步,执行的是__lookup_processor_type,这个函数是检查处理器型号,它读取你的电路板的CPU型号与内核支持的处理器进行比较看是否能够处理。这个我们不关心它的具体实现过程,因为现在主流处理器内核都提供了支持。

第二步,执行的是__lookup_machine_type,这个函数是来检查机器型号的,它会读取你bootloader传进来的机器ID和他能够处理的机器ID进行比较看是否能够处理。内核的ID号定义在arc/arm/tool/mach_types文件中MACH_TYPE_xxxx宏定义。内核究竟就如何检查是否是它支持的机器的呢?实际上每个机器都会在/arc/arm/mach-xxxx/smdk-xxxx.c文件中有个描述特定机器的数据结构,如下

[cpp] view
plain
?
  1. 01.MACHINE_START(S3C2440,"SMDK2440")
  2. 02.       /* Maintainer: Ben Dooks<ben@fluff.org> */
  3. 03.       .phys_io  =S3C2410_PA_UART,
  4. 04.       .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
  5. 05.       .boot_params  = S3C2410_SDRAM_PA + 0x100,
  6. 06.
  7. 07.       .init_irq   =s3c24xx_init_irq,
  8. 08.       .map_io          =smdk2440_map_io,
  9. 09.       .init_machine  = smdk2440_machine_init,
  10. 10.       .timer             =&s3c24xx_timer,
  11. 11.MACHINE_END
  12. 12.

MACHINE_START和 MACHINE_END实际上被展开成一个结构体

[cpp] view
plain
?
  1. #defineMACHINE_START(_type,_name)                 \
  2. staticconst struct machine_desc __mach_desc_##_type       \
  3. __used                                             \
  4. __attribute__((__section__(".arch.info.init")))= {    \
  5. .nr          =MACH_TYPE_##_type,           \
  6. .name             =_name,
  7. #defineMACHINE_END                          \
  8. };

于是上面的数据结构就被展开为

[cpp] view
plain
?
  1. 01.staticconst struct machine_desc __mach_desc_S3C2440     \
  2. 02. __used                                             \
  3. 03. __attribute__((__section__(".arch.info.init")))= {    \
  4. 04.       .nr          =MACH_TYPE_S3C2440,          \
  5. 05.       .name             =”SMDK2440”,};
  6. 06..phys_io  = S3C2410_PA_UART,
  7. 07.       .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
  8. 08.       .boot_params  = S3C2410_SDRAM_PA + 0x100,
  9. 09.
  10. 10.       .init_irq   =s3c24xx_init_irq,
  11. 11.       .map_io          =smdk2440_map_io,
  12. 12.       .init_machine  = smdk2440_machine_init,
  13. 13.       .timer             =&s3c24xx_timer,
  14. 14.
  15. 15.}

每个机器都会有一个machine_desc__mach_desc结构,内核通过检查每个machine_desc__mach_desc的nr号和bootloader传上来的ID进行比较,如果相同,内核就认为支持该机器,而且内核在后面的工作中会调用该机器的machine_desc__mach_desc_结构中的方法进行一些初始化工作。

第三步,创建一级页表。

第四步,在R13中保存__switch_data 这个函数的地址,在第四步使能mmu完成后会跳到该函数执行。

第五步,执行的是__enable_mmu,它是使能MMU,这个函数调用了__turn_mmu_on函数,让后在_turn_mmu_on在最后将第三步赋给R13的值传给了PC指针 (mov    pc, r13),于是内核开始跳到__switch_data这个函数开始执行。

我们再来看arch/arm/kenel/head-common.S这个文件中的__switch_data函数

[cpp] view
plain
?
  1. 01.__switch_data:
  2. 02.       .long       __mmap_switched
  3. 03.       .long       __data_loc                    @ r4
  4. 04.       .long       __data_start                  @ r5
  5. 05.       .long       __bss_start                    @ r6
  6. 06.       .long       _end                            @ r7
  7. 07.       .long       processor_id                 @ r4
  8. 08.       .long       __machine_arch_type           @ r5
  9. 09.       .long       cr_alignment                 @ r6
  10. 10.       .long       init_thread_union+ THREAD_START_SP @ sp
  11. 11.
  12. 12./*
  13. 13. * The following fragment of code is executedwith the MMU on in MMU mode,
  14. 14. * and uses absolute addresses; this is notposition independent.
  15. 15. *
  16. 16. *  r0  =cp#15 control register
  17. 17. * r1  = machine ID
  18. 18. * r9  = processor ID
  19. 19. */
  20. 20.       .type       __mmap_switched,%function
  21. 21.__mmap_switched:
  22. 22.       adr   r3,__switch_data + 4
  23. 23.
  24. 24.       ldmia      r3!,{r4, r5, r6, r7}
  25. 25.       cmp r4,r5                           @ Copy datasegment if needed
  26. 26.1:    cmpne     r5,r6
  27. 27.       ldrne       fp,[r4], #4
  28. 28.       strne       fp,[r5], #4
  29. 29.       bne  1b
  30. 30.
  31. 31.       mov fp,#0                           @ Clear BSS(and zero fp)
  32. 32.1:    cmp r6,r7
  33. 33.       strcc fp,[r6],#4
  34. 34.       bcc  1b
  35. 35.
  36. 36.       ldmia      r3,{r4, r5, r6, sp}
  37. 37.       str    r9, [r4]                  @ Save processor ID
  38. 38.       str    r1, [r5]                  @ Save machine type
  39. 39.       bic   r4,r0, #CR_A               @ Clear 'A' bit
  40. 40.       stmia       r6,{r0, r4}                   @ Save controlregister values
  41. 41.       b     start_kernel

这个函数做的工作是,复制数据段清楚BBS段,设置堆在指针,然后保存处理器内核和机器内核等工作,最后跳到start_kernel函数。于是内核开始执行第二阶段。

第二阶段:

 

我们再来看init/目录下的main.c的start_kernel函数,这里我只截图了部分。

[cpp] view
plain
?
  1. 01.asmlinkage void __init start_kernel(void)
  2. 02.{
  3. 03.       …………………….
  4. 04.       ……………………..
  5. 05.       printk(KERN_NOTICE);
  6. 06.       printk(linux_banner);
  7. 07.       setup_arch(&command_line);
  8. 08.       setup_command_line(command_line);
  9. 09.
  10. 10.
  11. 11.       parse_early_param();
  12. 12.       parse_args("Booting kernel",static_command_line, __start___param,
  13. 13.                __stop___param - __start___param,
  14. 14.                &unknown_bootoption);
  15. 15.……………………
  16. 16.…………………………
  17. 17.       init_IRQ();
  18. 18.       pidhash_init();
  19. 19.       init_timers();
  20. 20.       hrtimers_init();
  21. 21.       softirq_init();
  22. 22.       timekeeping_init();
  23. 23.       time_init();
  24. 24.       profile_init();
  25. 25.…………………………
  26. 26.……………………………
  27. 27.       console_init();
  28. 28.………………………………
  29. 29.………………………………
  30. 30.       rest_init();
  31. 31.}

从上面可以看出start_kernel首先是打印内核信息,然后对bootloader传进来的一些参数进行处理,再接着执行各种各样的初始化,在这其中会初始化控制台。最后会调用rest_init();这个函数会启动挂接根文件系统并且启动init进程。

综上,内核启动的过程大致为以下几步:

1.检查CPU和机器类型

2.进行堆栈、MMU等其他程序运行关键的东西进行初始化

3.打印内核信息

4.执行各种模块的初始化

5.挂接根文件系统

6.启动第一个init进程

ARM-Linux内核移植之(一)——内核启动流程分析的更多相关文章

  1. 从0移植uboot (二) _启动流程分析

    经过了上一篇的配置,我们已经执行make就可以编译出一个uboot.bin,但这还不够,首先,此时的uboot并不符合三星芯片对bootloader的格式要求,其次,此时的uboot.bin也没有结合 ...

  2. 从0移植uboot (二) _uboot启动流程分析

    经过了上一篇的配置,我们已经执行make就可以编译出一个uboot.bin,但这还不够,首先,此时的uboot并不符合三星芯片对bootloader的格式要求,同时,此时的uboot.bin也没有结合 ...

  3. (转)从0移植uboot (二) _uboot启动流程分析

    ref:https://www.cnblogs.com/xiaojiang1025/p/6496704.html 经过了上一篇的配置,我们已经执行make就可以编译出一个uboot.bin,但这还不够 ...

  4. linux根文件系统制作,busybox启动流程分析

    分析 busybox-1.1.6 启动流程,并 制作一个小的根文件系统 源码百度云链接:https://pan.baidu.com/s/1tJhwctqj4VB4IpuKCA9m1g 提取码 :l10 ...

  5. arm linux kernel 从入口到start_kernel 的代码分析

    参考资料: <ARM体系结构与编程> <嵌入式Linux应用开发完全手册> Linux_Memory_Address_Mapping http://www.chinaunix. ...

  6. 鸟哥的linux私房菜——第20章 启动流程、模块管理与loader

    20.1 Linux启动流程分析 Linux启动过程: 按下开机电源后计算机硬件主动读取BIOS来加载硬件信息以及硬件系统的自我测试,之后系统会主动读取第一个可启动的设备(由BIOS设置),此时就可以 ...

  7. Uboot启动流程分析(转载)

    最近一段时间一直在做uboot移植相关的工作,需要将uboot-2016-7移植到单位设计的ARMv7的处理器上.正好元旦放假三天闲来无事,有段完整的时间来整理下最近的工作成果.之前在学习uboot时 ...

  8. imx6 uboot启动流程分析

    参考http://blog.csdn.net/skyflying2012/article/details/25804209 这里以imx6平台为例,分析uboot启动流程对于任何程序,入口函数是在链接 ...

  9. Uboot启动流程分析(三)

    1.前言 在前面的文章Uboot启动流程分析(二)中,链接如下: https://www.cnblogs.com/Cqlismy/p/12002764.html 已经对_main函数的整个大体调用流程 ...

  10. Uboot启动流程分析(一)

    1.前言 Linux系统的启动需要一个bootloader程序,该bootloader程序会先初始化DDR等外设,然后将Linux内核从flash中拷贝到DDR中,最后启动Linux内核,uboot的 ...

随机推荐

  1. netty同时支持socket和http

    项目需要使用netty做中转服务器,同时支持两种不同协议的客户端,经过几天查询资料终于找到合适的方案了,同时感谢Netty权威指南及论坛问答,开始贴代码 客户端1==>socket public ...

  2. 六 web爬虫讲解2—urllib库爬虫—基础使用—超时设置—自动模拟http请求

    利用python系统自带的urllib库写简单爬虫 urlopen()获取一个URL的html源码read()读出html源码内容decode("utf-8")将字节转化成字符串 ...

  3. IOS-CoreLocation

    一.简介 在移动互联网时代,移动app能解决用户的很多生活琐事,比如 导航:去任意陌生的地方 周边:找餐馆.找酒店.找银行.找电影院   在上述应用中,都用到了地图和定位功能,在iOS开发中,要想加入 ...

  4. day5-shutil模块

    一.概述 我们通过python操作文件时,除正常读写操作外,有时还需要进行拷贝.删除.打包等操作,虽然os模块提供了部分功能,但还是不够完善,这里要讲讲专业的高级的文件,文件夹,压缩包处理模块shut ...

  5. PowerDesigner 概念数据模型

    (转自:http://www.cnblogs.com/yxonline/archive/2007/04/09/705479.html) 目标:本文主要介绍PowerDesigner中概念数据模型 CD ...

  6. ARM汇编指令集1

    (汇编)指令是CPU机器指令的助记符,经过编译过会得到一串0011组成的机器码,可以由CPU读取执行. (汇编)伪指令本质不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译 ...

  7. 下载并安装Prism5.0库 Download and Setup Prism Library 5.0 for WPF(英汉对照版)

    Learn what’s included in Prism 5.0 including the documentation, WPF code samples, and libraries. Add ...

  8. 用工具快速建立hibernate框架

    ,一.建好项目后先导入两类jar包,一类是hibernate的jar包,一类是jdbc的jar包 二.点击“窗口”--“显示视图”--“其它”-“Hibernate configurations” 三 ...

  9. 《Java程序员职场全攻略 从小工到专家》 - 书摘精要

    (前言) 学习招式在次,提升内力才是最主要的: (P10) 选择一门编程语言,只是入门的途径.过分依赖编程语言,只会让自己成为代码高手,而不是开发大牛,要知道编程语言只是一种工具,更重要的是编程思想: ...

  10. L134

    这种成功和后来的研究(表明记忆本身并不是先天决定的)使爱立信总结到,记忆的行为与其说是一种习得的行为不如说是一种先天的行为. 这点我们不清楚-构思物体和找出数字模型的能力,回答问题(最好的诗人和哲学家 ...