Linux移植之内核启动过程引导阶段分析

发布时间:2024-08-26  

在Linux移植之make uImage编译过程分析中已经提到了uImage是一个压缩的包并且内含压缩程序,可以进行自解压。自解压完成之后内核代码从物理地址为0x30008000处开始运行。下面分析在进入C之前内核做的一些工作,以下是内核启动过程中打印出来的信息,其中Uncompressing Linux就是在自解压代码。make uImage编译的最后也给出了链接脚本arch/arm/kernel/vmlinux.lds,以及链接的顺序arch/arm/kernel/head.o 是第一个。

分析arch/arm/kernel/vmlinux.lds可以知道程序入口的地址是stext,并且是.text.head段


277    OUTPUT_ARCH(arm)

278    ENTRY(stext)


291    . = (0xc0000000) + 0x00008000;

292

293    .text.head : {

294    _stext = .;

295    _sinittext = .;

296    *(.text.head)

297    }


打开arch/arm/kernel/head.s。可见内核运行的第一条代码就是第79行的代码,从这条开始分析,首先将CPU设置为管理模式,并且关闭所有中断;然后获得CPU的id。


76        .section '.text.head', 'ax' //.text.head段

77        .type    stext, %function

78    ENTRY(stext)                    //入口地址stext

79        msr    cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode//确保进入了管理模式

80                            @ and irqs disabled                                     //并且禁止中断

81        mrc    p15, 0, r9, c0, c0        @ get processor id                               //获得处理器的CPU id,并且存入 r9中

82        bl    __lookup_processor_type        @ r5=procinfo r9=cpuid           //调用函数,输入参数r9=cpuid。返回值r5=procinfo

83        movs    r10, r5                @ invalid processor (r5=0)?//如果不支持当前CPU,则r5=0

84        beq    __error_p            @ yes, error 'p'                              //如果r5=0,则打印错误

85        bl    __lookup_machine_type        @ r5=machinfo          //调用函数,r5=返回值machinfo

86        movs    r8, r5                @ invalid machine (r5=0)?  //如果不支持当前单板,则返回r5=0

87        beq    __error_a            @ yes, error 'a'               //如果r5=0,则打印错误

88        bl    __create_page_tables//创建一级页表以建立虚拟地址到物理地址的映射关系,后面再研究


接着调用__lookup_processor_type,它位于archarmkernelhead-common.S。它的功能是比较当前CPU的id与内核支持的CPU的id是否相符合。这段代码在.proc.info.init段中从__proc_info_begin开始到__proc_info_end结束,寻找符合当前CPU的ID号的proc_info_list结构


145        .type    __lookup_processor_type, %function

146    __lookup_processor_type:

147        adr    r3, 3f                        //r3 = 第178行代码的物理地址

148        ldmda    r3, {r5 - r7}      //将r3地址开始的3个地址的内容赋给 r5、r6、r7 ;r5=__proc_info_begin,r6=__proc_info_end

149        sub    r3, r3, r7            @ get offset between virt&phys//r3=r3-r7,即物理地址与虚拟地址的差值

150        add    r5, r5, r3            @ convert virt addresses to//r5=__proc_info_begind对应的物理地址

151        add    r6, r6, r3            @ physical address space   //r6=__proc_info_end对应的物理地址

152    1:    ldmia    r5, {r3, r4}            @ value, mask//r3、r4等于proc_info_list结构中的cpu_val、cpu_mask

153        and    r4, r4, r9            @ mask wanted bits//r4=r4&r9=cpu_mask&传入的cpuid

154        teq    r3, r4                                                    //比较

155        beq    2f                                                        //如果相等,则找到对应的proc_info_list结构,跳到160行

156        add    r5, r5, #PROC_INFO_SZ        @ sizeof(proc_info_list)//r5指向下一个proc_info_list结构

157        cmp    r5, r6                                                   //是否已经比较完所有proc_info_list

158        blo    1b                                                       //没有则继续比较

159        mov    r5, #0                @ unknown processor//比较完毕,但是没有找到匹配的proc_info_list结构,r5=0

160    2:    mov    pc, lr//返回,返回的值为r5=proc_info_list


176        .long    __proc_info_begin

177        .long    __proc_info_end

178    3:    .long    .//.表示当前这条代码链接后的虚拟地址

179        .long    __arch_info_begin

180        .long    __arch_info_end


其中__proc_info_begin、__proc_info_end被定义在archarmkernelvmlinux.lds中,它的意思是内核源码中有被定义为.proc.info.init的内容,它的起始地址是__proc_info_begin,结束地址为__proc_info_end。


299     .init : { /* Init code and data        */

230       *(.init.text)

231      _einittext = .;

232      __proc_info_begin = .;

233       *(.proc.info.init)

234      __proc_info_end = .;


接着看到proc_info_list结构的内容,它被定义在includeasm-armProcinfo.h中


29    struct proc_info_list {

30        unsigned int        cpu_val;      

31        unsigned int        cpu_mask;

32        unsigned long        __cpu_mm_mmu_flags;    /* used by head.S */

33        unsigned long        __cpu_io_mmu_flags;    /* used by head.S */

34        unsigned long        __cpu_flush;        /* used by head.S */

35        const char        *arch_name;

36        const char        *elf_name;

37        unsigned int        elf_hwcap;

38        const char        *cpu_name;

39        struct processor    *proc;

40        struct cpu_tlb_fns    *tlb;

41        struct cpu_user_fns    *user;

42        struct cpu_cache_fns    *cache;

43    };


接着找到对于当前内核支持的proc_info_list 定义,它在archarmmmproc-arm920.S 中。对于S3C2410、S3C2440芯片来说CPU ID都是0x41129200。cpu_val的值为0x41009200、cpu_mask的值为0xff00fff0,刚好匹配。


 .section '.proc.info.init', #alloc, #execinstr


448        .type    __arm920_proc_info,#object

449    __arm920_proc_info:

450        .long    0x41009200//cpu_val值

451        .long    0xff00fff0//cpu_mask值

452        .long   PMD_TYPE_SECT |

453            PMD_SECT_BUFFERABLE |

454            PMD_SECT_CACHEABLE |

455            PMD_BIT4 |

456            PMD_SECT_AP_WRITE |

457            PMD_SECT_AP_READ

458        .long   PMD_TYPE_SECT |

459            PMD_BIT4 |

460            PMD_SECT_AP_WRITE |

461            PMD_SECT_AP_READ

462        b    __arm920_setup

463        .long    cpu_arch_name

464        .long    cpu_elf_name

465        .long    HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB

466        .long    cpu_arm920_name

467        .long    arm920_processor_functions

468        .long    v4wbi_tlb_fns

469        .long    v4wb_user_fns

470    #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH

471        .long    arm920_cache_fns

472    #else

473        .long    v4wt_cache_fns

474    #endif

475        .size    __arm920_proc_info, . - __arm920_proc_info


继续回到arch/arm/kernel/head.s往下分析,看到第83行,调用完__lookup_processor_type后r5的值变为执向找到的proc_info_list 结构的地址。所以第83行与第84行比较r5是否为0,如果为0说明没有找到符合当前CPU的ID号,则打印错误。接着到85行,调用__lookup_machine_type,它同样位于archarmkernelhead-common.S中,它的功能是比较当前单板的id与内核支持的单板的id是否相符合。这段代码在.arch.info.init段中从__arch_info_begin开始到__arch_info_end结束,寻找符合当前单板的ID号的machine_desc结构


176        .long    __proc_info_begin

177        .long    __proc_info_end

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>