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

在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,它位于arch\arm\kernel\head-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_list176        .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被定义在arch\arm\kernel\vmlinux.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结构的内容,它被定义在include\asm-arm\Procinfo.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 定义,它在arch\arm\mm\proc-arm920.S 中。对于S3C2410、S3C2440芯片来说CPU ID都是0x41129200。cpu_val的值为0x41009200、cpu_mask的值为0xff00fff0,刚好匹配。

    .section ".proc.info.init", #alloc, #execinstr448        .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,它同样位于arch\arm\kernel\head-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
178    3:    .long    .//.表示当前这条代码链接后的虚拟地址
179        .long    __arch_info_begin
180        .long    __arch_info_end193        .type    __lookup_machine_type, %function
194    __lookup_machine_type:
195        adr    r3, 3b                   //r3=第178行的物理地址
196        ldmia    r3, {r4, r5, r6}     //r4=r3。r5=__proc_info_end,r6=__proc_info_begin,取得的是虚拟地址
197        sub    r3, r3, r4            @ get offset between virt&phys//r3=r3-r4,取得物理地址与虚拟地址的偏差
198        add    r5, r5, r3            @ convert virt addresses to//r5=r5+r3,取得物理地址__proc_info_end
199        add    r6, r6, r3            @ physical address space   //r6=r6+r3,取得物理地址__proc_info_begin
200    1:    ldr    r3, [r5, #MACHINFO_TYPE]    @ get machine type //r3=取得单板的编号
201        teq    r3, r1                @ matches loader number?//比较r3与r1是否相等,即linux是否支持uboot传入的单板
202        beq    2f                @ found                     //如果相等,则跳到207行,找到支持的单板,返回
203        add    r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc//r5执向下一个machine_desc结构
204        cmp    r5, r6                                             //是否已经比较完machine_desc结构?
205        blo    1b                                                 //如果没有比较完,则跳到200行继续比较
206        mov    r5, #0                @ unknown machine              //如果所有machine_desc都比较完了,r5=0
207    2:    mov    pc, lr                                             //返回

其中__arch_info_begin、__arch_info_end被定义在arch\arm\kernel\vmlinux.lds中,它的意思是内核源码中有被定义为.arch.info.init的内容,它的起始地址是__arch_info_begin,结束地址为__arch_info_end。

305      __arch_info_begin = .;
306       *(.arch.info.init)
307      __arch_info_end = .;

接着看到machine_desc结构的内容,它被定义在include\asm-arm\mach\Arch.h 中

17    struct machine_desc {
18        /*
19         * Note! The first four elements are used
20         * by assembler code in head-armv.S
21         */
22        unsigned int        nr;        /* architecture number    */     //单板的编号,是从内核传过来的编号  r1
23        unsigned int        phys_io;    /* start of physical io    */
24        unsigned int        io_pg_offst;    /* byte offset for io 
25                             * page tabe entry    */
26
27        const char        *name;        /* architecture name    */
28        unsigned long        boot_params;    /* tagged list        *///boo传过来的tag标记的位置,也是从内核传过来的 r2
29
30        unsigned int        video_start;    /* start of video RAM    */
31        unsigned int        video_end;    /* end of video RAM    */
32
33        unsigned int        reserve_lp0 :1;    /* never has lp0    */
34        unsigned int        reserve_lp1 :1;    /* never has lp1    */
35        unsigned int        reserve_lp2 :1;    /* never has lp2    */
36        unsigned int        soft_reboot :1;    /* soft reboot        */
37        void            (*fixup)(struct machine_desc *,
38                         struct tag *, char **,
39                         struct meminfo *);
40        void            (*map_io)(void);/* IO mapping function    *///IO映射函数,移植时需要关注
41        void            (*init_irq)(void);
42        struct sys_timer    *timer;        /* system tick timer    */
43        void            (*init_machine)(void);
44    };

接着需要找到对于当前内核支持的machine_desc定义,在include\asm-arm\mach\Arch.h 中有如下宏定义,它表示在.arch.info.init段存入一个machine_desc 的结构体,名称为

__mach_desc_type,结构体内.nr、.name初始化为MACH_TYPE_type、_name
50    #define MACHINE_START(_type,_name)            \
51    static const struct machine_desc __mach_desc_##_type    \
52     __used                            \
53     __attribute__((__section__(".arch.info.init"))) = {    \
54        .nr        = MACH_TYPE_##_type,        \
55        .name        = _name,
56    
57    #define MACHINE_END                \
58    };

接着找调用MACHINE_START这个宏的文件,在arch\arm\mach-s3c2440\Mach-smdk2440.c 找到了,所以单板的ID为MACH_TYPE_S3C2440,它被定义在include\asm-arm\Mach-types.h中

#define MACH_TYPE_S3C2440              362。与UBOOT传入的参数相符合。

339    MACHINE_START(S3C2440, "SMDK2440")
340        /* Maintainer: Ben Dooks <ben@fluff.org> */
341        .phys_io    = S3C2410_PA_UART,
342        .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
343        .boot_params    = S3C2410_SDRAM_PA + 0x100,
344
345        .init_irq    = s3c24xx_init_irq,
346        .map_io        = smdk2440_map_io,
347        .init_machine    = smdk2440_machine_init,
348        .timer        = &s3c24xx_timer,
349    MACHINE_END

继续来看MACHINE_START(S3C2440, "SMDK2440")这个宏,在里面有许多和开发板相关的设置,比如说smdk2440_map_io,它被定义在arch\arm\mach-s3c2440\Mach-smdk2440.c中,在Linux移植之移植步骤中提到过想要移植成功,必须修改327行代码,将晶振的设置改为12000000。还有其它的一些配置就不一一列举了。

324    static void __init smdk2440_map_io(void)
325    {
326        s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
327        s3c24xx_init_clocks(12000000);//根据开发板合适的晶振配置
328        s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
329    }

回到arch/arm/kernel/head.s接着往下看,86、87行判断__lookup_machine_type是否成功找到支持单板的machine_desc结构,如果没找到则打印错误,88行是用来创建一级页表以建立虚拟地址到物理地址的映射关系,这里不详细分析。

继续往下看,看到100行,其中r10的值为__arm920_proc_info所在地址,PROCINFO_INITFUNC为proc_info_list结构体的偏移量,具体为__cpu_flush,对应到__arm920_proc_info结构体内,pc的值就是b __arm920_setup这条语句所在地址,即执行b __arm920_setup这条指令,__arm920_setup做一些MMU相关的初始化,在arch\arm\mm\proc-arm920.S中,这里不做细究。

97    ldr    r13, __switch_data        @ address to jump to after//r13是堆栈寄存器sp
98                            @ mmu has been enabled
99    adr    lr, __enable_mmu        @ return (PIC) address //100行设置完成之后在使能MMU
100   add    pc, r10, #PROCINFO_INITFUNC//调用__arm920_setup函数,应该跟MMU相关,后面再研究

b __arm920_setup执行完毕返回之后执行的是arch/arm/kernel/head.s下的__enable_mmu 。

152        .type    __enable_mmu, %function
153    __enable_mmu:
.....
174        b    __turn_mmu_on187        .align    5
188        .type    __turn_mmu_on, %function
189    __turn_mmu_on:
190        mov    r0, r0
191        mrc    p15, 0, r3, c0, c0, 0        @ read id reg
192        mov    r3, r3
193        mov    r3, r3
194        mov    pc, r13//设置完MMU之后跳转到__switch_data执行

__enable_mmu 执行完之后进入__switch_data执行,注意这时候的运行地址已经是初始化MMU之后的虚拟地址了。从15-24行可以看出pc=__mmap_switched,__mmap_switched的主要工作是将processor_id与__machine_arch_type初始化为当前MCU的编号与单板的编号

14        .type    __switch_data, %object
15    __switch_data:
16        .long    __mmap_switched
17        .long    __data_loc            @ r4
18        .long    __data_start            @ r5
19        .long    __bss_start            @ r6
20        .long    _end                @ r7
21        .long    processor_id            @ r4//之前找到的符合当前MCU的__arm920_proc_info结构体
22        .long    __machine_arch_type        @ r5//之前找到的符合单板的__mach_desc_S3C2440结构体
23        .long    cr_alignment            @ r6
24        .long    init_thread_union + THREAD_START_SP @ sp
25
26    /*
27     * The following fragment of code is executed with the MMU on in MMU mode,
28     * and uses absolute addresses; this is not position independent.
29     *
30     *  r0  = cp#15 control register
31     *  r1  = machine ID
32     *  r9  = processor ID
33     */
34        .type    __mmap_switched, %function
35    __mmap_switched://虚拟地址已经可以使用
36        adr    r3, __switch_data + 4//r3=__data_loc所在的地址
37
38        ldmia    r3!, {r4, r5, r6, r7}//r4=__data_loc所在地址;r5=__data_start所在地址依次类推 r3=__switch_data+4*4
39        cmp    r4, r5                @ Copy data segment if needed //检查是否有__data_loc段r4=r5说明没有__data_loc
40    1:    cmpne    r5, r6          
41        ldrne    fp, [r4], #4
42        strne    fp, [r5], #4
43        bne    1b
44
45        mov    fp, #0                @ Clear BSS (and zero fp)//清0BSS段
46    1:    cmp    r6, r7
47        strcc    fp, [r6],#4
48        bcc    1b
49
50        ldmia    r3, {r4, r5, r6, sp}//r4=processor_id、r5=__machine_arch_type、r6=cr_alignment、sp=init_thread_union + THREAD_START_SP
51        str    r9, [r4]            @ Save processor ID//processor_id=r9 = proc_info_list.cpu_val = 0x41009200
52        str    r1, [r5]            @ Save machine type//__machine_arch_type=r1 = machine_desc .nr =  MACH_TYPE_S3C2440 = 362
53        bic    r4, r0, #CR_A            @ Clear 'A' bit
54        stmia    r6, {r0, r4}            @ Save control register values
55        b    start_kernel//跳转到start_kernel C函数

最终执行b start_kernel,跳到C函数,这是第二阶段的内容。

 

转载于:https://www.cnblogs.com/andyfly/p/9404198.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279720.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

outlook附件大小限制_如何在Outlook中调整大图片附件的大小

outlook附件大小限制When you have to send someone a large image file through email, it’s a good idea to resize the image file to make it smaller before sending it. Outlook makes this easy and allows you to resize the image file as it’s sent. 当您必须通过电…

西湖论剑WP

先水几句&#xff0c;这次的题确实难啊&#xff0c;动用洪荒之力了&#xff0c;第一名的神仙也没有全部做完。 官方说这次的题目有两道没被做出来&#xff0c;我猜应该是PWN和RE吧 本来我们是45名的&#xff0c;最后5分钟那帮人啊&#xff0c;硬生生给我们挤出前50&#xff0c;…

vm macos 启用3d_如何在macOS中启用夜班以减轻眼睛疲劳

vm macos 启用3dNight Shift is a new feature introduced in macOS Sierra 10.12.4, and you might already be familiar with it if you’re an iOS user. Here’s how to enable it and set it up on your Mac. Night Shift是macOS Sierra 10.12.4中引入的新功能&#xff0c…

如何在Windows 7、8、10,Vista或XP中删除Windows服务

If you are a fan of tweaking your system and disabling services, you might find that over time your Windows Services list becomes large and unwieldy. It’s easy enough to delete a Windows service using the Command Prompt. 如果您喜欢调整系统并禁用服务&#…

缩点(有向图的强连通分量)学习笔记

缩点(有向图的强连通分量)学习笔记 1.什么是强连通分量&#xff1f;&#xff1a; 有向图强连通分量:在有向图G中&#xff0c;如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径&#xff0c;同时还有一条从vj到vi的有向路径&#xff0c;则称两个顶点强连通(strongly conne…

mysql多表联合删除

文件一&#xff1a;01.txt文件二&#xff1a;02.txt登录mysql选择数据库表user结构表user_depart结构导入数据到表user导入数据到表user_depart联合删除查看删除后user表的数据查看删除后user_depart表的数据本文转自 Lee_吉 51CTO博客&#xff0c;原文链接:http://blog.51cto.…

centos 初学者_初学者:如何在Outlook 2013中创建和管理任务

centos 初学者If you’re one of those people who has a whiteboard or notepad with an ever-evolving to-do list, or your desk and monitors are adorned with Post-its reminding you of important events, then this the article for you. 如果您是拥有不断发展的待办事…

新服务器安装和配置zabbix的playbook

公司的金山区云服务器是由我负责的&#xff0c;每一次新购买了金山区的服务器都要把这些新服务器添加到zabbix监控里&#xff0c;于是我就编写了一个ansible的playbook&#xff0c;这样以后就可以在执行playbook的时候“带薪拉屎”了。 ansible主机准备&#xff1a; 1&#xff…

15个变态的Google面试题以及答案

在当前经济形势不景气的情况下&#xff0c;谷歌招聘新员工是一件令人振奋的事&#xff0c;特别是对那些在当前金融风暴中渴望找到安全港的年轻经理们和软件开发商们来说是个好消息。   不过&#xff0c;也不要高兴太早&#xff0c;谷歌在招聘新员工时&#xff0c;更加青睐名牌…

小程序禁用ios 左右滑动_如何在使用应用程序时禁用iOS控制中心

小程序禁用ios 左右滑动The Control Center has proven to be a thoughtful and welcome addition to iOS, but it can be annoying sometimes if you’re playing a game or using an app, and you accidentally open it. Here’s how you can disable it in such situations.…

repomd.xml错误14 not found

用Centos7最小化安装了系统&#xff0c;想练练手&#xff0c;可以到换了“搜狐”的YUM源&#xff0c;系统总报错更新错误说找不到repomd.xml。 然后就一直搜解决问题&#xff0c;能用到的都用到了&#xff0c;网上提到的都用到了。浪费了好几个小时没解决。正当无语的时候&…

超链接禁用_如何在Microsoft Word中禁用超链接

超链接禁用When you type a web or email address in Word, you may notice that the program automatically formats it as a live hyperlink. This is a setting in Word’s AutoFormat feature that is on by default but can be easily turned off. 当您在Word中键入网站或…

ssh面试题总结

题目1&#xff1a;Hibernate工作原理及为什么要用&#xff1f; 原理&#xff1a; hibernate&#xff0c;通过对jdbc进行封装&#xff0c;对 java类和 关系数据库进行mapping&#xff0c;实现了对关系数据库的面向对象方式的操作&#xff0c;改变了传统的jdbc sql操作数据的方式…

xbox可以录视频声音吗_什么是Xbox Live Gold,它值得吗?

xbox可以录视频声音吗If you have an Xbox One or Xbox 360, Microsoft’s Xbox Live Gold service is required to play multiplayer games online. A subscription costs $10 per month or $60 per year. Xbox Live Gold also includes additional benefits, like free games…

显示器选三星还是飞利浦_如何为飞利浦色相灯设置计时器

显示器选三星还是飞利浦Maybe you want to turn off your Philips Hue lights after a certain amount of time has passed, or have them blink as a reminder. Whatever your needs, here’s how to set a timer for your Philips Hue lights to have them automatically tur…

PIE SDK与OpenCV结合说明文档

1.功能简介 OpenCV是基于BSD许可&#xff08;开源&#xff09;发行的跨平台计算机视觉库&#xff0c;可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C 类构成&#xff0c;同时提供了Python、Ruby、MATLAB等语言的接口&…

js的栈堆与浅拷贝、深拷贝的理解

一&#xff1a;什么是堆栈&#xff1f; 我们都知道&#xff1a;在计算机领域中&#xff0c;堆栈是两种数据结构&#xff0c;它们只能在一端(称为栈顶(top))对数据项进行插入和删除。 堆&#xff1a;队列优先,先进先出&#xff1b;由操作系统自动分配释放 &#xff0c;存放函数的…

ea 备份码是什么_EA的原始访问是什么,值得吗?

ea 备份码是什么EA’s Origin Access gives you access to more than 70 games, discounts, and new EA games before they’re released for a monthly (or yearly) subscription fee. But is it really worth it? EA的Origin Access可让您访问70多种游戏&#xff0c;打折游戏…

JS框架_(JQuery.js)纯css3进度条动画

百度云盘  传送门  密码&#xff1a;wirc 进度条动画效果&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge,chrome1">…

如何在Android主屏幕上添加热点快捷方式

Portable Wi-Fi hotspots on your Android phone are great, because hotel Wi-Fi usually isn’t, but toggling that hotspot on and off is a pain. Here are several easy ways to add a hotspot widget to your home screen. 您的Android手机上的便携式Wi-Fi热点很棒&…