首页 (很好的嵌入式linux启动过程分析

(很好的嵌入式linux启动过程分析

举报
开通vip

(很好的嵌入式linux启动过程分析关于嵌入式Linux系统的启动(很好的嵌入式linux启动过程分析) 收藏 lw:虽然zImage的开头是head_armv.S的8个空循环,但是实际是先执行compressed/head.S进行解压操作。 ================================= #1 关于嵌入式系统的启动(SHARE FOR ALL) 嵌入式Linux启动分为两个部分,系统引导与Linux启动。系统引导将完成Linux装入内存前,初始化CPU和相关IO设备,并将Linux调入内存的工作。系统引导主要由Boo...

(很好的嵌入式linux启动过程分析
关于嵌入式Linux系统的启动(很好的嵌入式linux启动过程分析) 收藏 lw:虽然zImage的开头是head_armv.S的8个空循环,但是实际是先执行compressed/head.S进行解压操作。 ================================= #1 关于嵌入式系统的启动(SHARE FOR ALL) 嵌入式Linux启动分为两个部分,系统引导与Linux启动。系统引导将完成Linux装入内存前,初始化CPU和相关IO设备,并将Linux调入内存的工作。系统引导主要由BootLoader实现。在BootLoader将Linux内核调入内存之后,将权力交给LinuxKernel,进入Linux的启动部分。以下详细分析启动的过程与使用的文件。 一、系统引导与BootLoader BootLoader因嵌入式系统的不同与PC机有很大不同,这里将以Hyper250(Inter Xscale GDPXA250)的启动为例来分析。由于没有BIOS驱动主板,EnbeddedOS必须由bootloader驱动所有的硬件,并完成硬件的初始化工作。 所有的初始化文件在hyper250/Bootloader目录下。 首先分析开机运行的分件: hyper250/Bootloader/X-Hyper250R1.1-Boot/src/start_xscale.S 文件包含两个库文件: hyper250/Bootloader/X-Hyper250R1.1-Boot/src/include/config.h hyper250/Bootloader/X-Hyper250R1.1-Boot/src/include/start_xscale.h 文件config.h主要完成系统各硬件的宏定义与设定,xscale.h主要完成对系统芯片的及系统操作的设定。 以下分析config.h文件: (1)存储总线设备的宏定义:定义Flash的大小、字长等信息,定义SRAM的基址、大小和块大小。 (2)动态内存设定:定义DRAM的大小、基址。 (3)软件包信息:包名称、版本号。 (4)设定BOOT LOADER的位置:在DRAM和SRAM的最大值、DRAM装入位置、栈的基址。 (5)设定kernel的位置:在DRAM和SRAM的基址、KERNEL的最大值、KERNEL中块的数量。 (6)设定文件系统的位置:根目录在DRAM和SRAM的基址、文件系统的最大值、文件系统中块的数量。 (7)设定LOADER程序:LOADER程序的静态内存基址、LOADER程序的最大值、块的数量。 (8)网络设定 以下分析start_xcalse.h文件: (1)定义内存基址(A0000000) (2)定义中断基址(40D00000)和中断保护栈的偏移量 (3)定义时钟管理基址(41300000)和寄存器偏移及其初始值 (4)定义GPIO接口寄存器基址(40E00000)及各寄存器的偏移 (5)定义GPIO接口各寄存器的初始值 (6)定义内存控制寄存器基址(48000000)和各寄存器的偏移 (7)定义内存控制寄存器的初始值 (8)定义电源管理寄存器的参数 (9)定义FFUART寄存器的基址(40100000)和各寄存器的偏移 (10)定义FFUART各寄存器的初始值 以下分析start_xcalse.S文件: (1)设定中断基址(40D00000),完成中断保护栈的初始化 (2)初始化GPIO接口 (3)初始化内存SDRAM (4)将Bootloader从Flash拷贝到SDRAM中 (5)装入Linux内核镜像,将内核从Flash(000C 0000)装入SDRAM(A0008000)中. (6)设定保护栈 (7)调用main.c的主函数c_main() 以上start_xcalse.S通过APCS的编程 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 书写的汇编文件初始化了系统相关的硬件,并且完成了BootLoader的装入内存和Linux内核的装入,最后将权力转交给main.c。 以下将分析main.c文件: hyper250/Bootloader/X-Hyper250R1.1-Boot/src/main.c 以及两个库文件 hyper250/Bootloader/X-Hyper250R1.1-Boot/src/include/main.h hyper250/Bootloader/X-Hyper250R1.1-Boot/src/include/scc.h #2 二、Linux启动过程分析 1.Makefile分析: 在分析arch/arm/boot/compressed目录下的文件的时候,对于Makefile的分析是很重要的,因为内核将在这个目录相产生。这里主要工作是对内核的压缩和解压工作。本目录在编译完成后将产生vmlinux、head.o、misc.o、head-xscale.o、piggy.o这几个文件。其中vmlinux是(没有--lw:zImage是压缩过的内核)压缩过的内核。head.o是内核的头部文件,负责初始设置。misc.o将主要负责内核的解压工作,它在head.o之后。head-xscale.o文件主要针对Xscale的初始化,将在链接时与head.o合并。piggy.o是一个中间文件,其实是一个压缩的内核,只不过没有和初始化文件及解压文件链接而已。 2.Decompress分析: 在BootLoader完成系统的引导以后并将Linux内核调入内存之后,调用bootLinux(),这个函数将跳转到kernel的起始位置。如果kernel没有压缩,就可以启动了。如果kernel压缩过,则要进行解压,在压缩过的kernel头部有解压程序。压缩过得kernel入口第一个文件源码位置在arch/arm/boot/compressed/head.S。它将调用函数decompress_kernel(),这个函数在文件arch/arm/boot/compressed/misc.c中,decompress_kernel()又调用proc_decomp_setup(),arch_decomp_setup()进行设置,然后使用在打印出信息“Uncompressing Linux...”后,调用gunzip()。将内核放于指定的位置。 启动首先运行的文件有: arch/arm/boot/compressed/head.S arch/arm/boot/compressed/head-xscale.S arch/arm/boot/compressed/misc.c 这些文件主要用于解压内核和以及启动内核映象。一旦内核启动,则这些文件所占内存空间将被释放。而且,一旦系统通过reset重起,当BootLoader将压缩过的内核放入内存中,首先执行的必然是这些代码。 以下分析head.S文件: (1)对于各种Arm CPU的DEBUG输出设定,通过定义宏来统一操作。 (2)设置kernel开始和结束地址,保存architecture ID。 (3)如果在ARM2以上的CPU中,用的是普通用户模式,则升到超级用户模式,然后关中断。 (4)分析LC0结构delta offset,判断是否需要重载内核地址(r0存入偏移量,判断r0是否为零)。 这里是否需要重载内核地址,我以为主要分析arch/arm/boot/Makefile、arch/arm/boot/compressed/Makefile和arch/arm/boot/compressed/vmlinux.lds.in三个文件,主要看vmlinux.lds.in链接文件的主要段的位置,LOAD_ADDR(_load_addr)=0xA0008000,而对于TEXT_START(_text、_start)的位置只设为0,BSS_START(__bss_start)=ALIGN(4)。对于这样的结果依赖于,对内核解压的运行方式,也就是说,内核解压前是在内存(RAM)中还是在FLASH上,因为这里,我们的BOOTLOADER将压缩内核(zImage)移到了RAM的0xA0008000位置,我们的压缩内核是在内存(RAM)从0xA0008000地址开始顺序排列,因此我们的r0获得的偏移量是载入地址(0xA0008000)。接下来的工作是要把内核镜像的相对地址转化为内存的物理地址,即重载内核地址。 (5)需要重载内核地址,将r0的偏移量加到BSS region和GOT table中。 (6)清空bss堆栈空间r2-r3。 (7)建立C程序运行需要的缓存,并赋于64K的栈空间。 (8)这时r2是缓存的结束地址,r4是kernel的最后执行地址,r5是kernel境象文件的开始地址。检查是否地址有冲突。 将r5等于r2,使decompress后的kernel地址就在64K的栈之后。 (9)调用文件misc.c的函数decompress_kernel(),解压内核于缓存结束的地方(r2地址之后)。此时各寄存器值有如下变化: r0为解压后kernel的大小 r4为kernel执行时的地址 r5为解压后kernel的起始地址 r6为CPU类型值(processor ID) r7为系统类型值(architecture ID) (10)将reloc_start代码拷贝之kernel之后(r5+r0之后),首先清除缓存,而后执行reloc_start。 (11)reloc_start将r5开始的kernel重载于r4地址处。 (12)清除cache内容,关闭cache,将r7中architecture ID赋于r1,执行r4开始的kernel代码。 关于head-xscale.S文件,它定义了xcale处理器的64k的cache缓存的实现代码和关闭MMU及缓存的代码,这些代码将在链接过程中与head.S的合并。 关于misc.c文件,它引入了以下几个文件: include/linux/kernel.h include/asm-arm/arch-pxa/uncompress.h include/asm-arm/proc-armv/uncompress.h include/asm-arm/uaccess.h lib/inflate.c 以下分析misc.c文件的decompress_kernel()函数: (1)首先传入参数:解压后内核地址,缓存开始地址,缓存结束地址,arch id。这些参数通过寄存器r0(r5),r1,r2,r3(r7)传入。 (2)接着执行proc_decomp_setup(),它在include/asm-arm/proc-armv/uncompress.h文件中。主要刷新并起用i cache,锁住交换缓存,这是一段嵌入的arm汇编代码。 (3)接着执行arch_decomp_setup(),它在include/asm-arm/arch-pxa/uncompress.h文件中,是一个空函数,用于扩展。 (4)然后执行makecrc(),它在lib/inflate.c中,主要将产生CRC-32 table,进行循环冗余校验。 (5)调用gunzip()解压kernel,它也在lib/inflate.c中。 (6)返回head.S,解压后kernel的长度传给r0,解压后的内核地址预先在r5中定义了。 #3 3.kernel进入文件分析: 随后系统将调入文件:arch/arm/kernel/head_armv.S或arch/arm/kernel/head_armo.S。对于arm的kernel而言,有两套.S文件:_armv.S 和 _armo.S. 选择_armv.S 还是_armo.S 依赖于处理器。ARM的version 1, version 2, 都只支持26位的地址空间。version 3开始支持32位的地址空间,同时还向后兼容26位的地址空间。version 4开始不再向后兼容26位的地址空间。这里由于Hyper250使用的是version7,故只涉及文件head_armv.S。 head_armv.S是内核的入口点,在内核被解压到预定位置后,它将运行,这里简要说明其主要工作: (1)首先,关中断并进入保护模式,这里将建立虚拟地址到物理地址的映射。(见第二章内存分析) (2)调用lookup_processor_type,查询CUP和其ID是否在.proc.info表中,如果存在,则令r10指向此结构,在CPU的内核入口文件中。如果不是则提示error:p并挂起。关于r10指向的结构,他所属的内核入口文件,以Hyper250为例:arch/arm/mm/proc-xcale.S。 这里要要注意的是,此处操作的对象是由vmlinux-armv.lds.in链接文件定位的段.proc.info中,这个段定义在proc-xcale.S文件末尾,这里要注意,上面并没有使系统进入保护模式,所以在这里对.proc.info寻址的时候,为了得到相对地址,做了一个相对寻址的变换。这里好象只用了这个结构的前3位:处理器类型值(value),处理器值掩码(mask),MMU标志值(mmuflags)。这3个值在分别放在寄存器r5(0x69052100)、r6(0xfffff7f0)、r8(0x00000c0e)中,r5和r6只是用于和获得的处理器的ID相比较,而r8则有两个可能的值,分别表示MMU的状态:如果MMU开启,即CACHE_WRITE_THROUGH,则r8=0x00000c0a,否则r8=0x00000c0e。这里r8的值将会保持到初始页表时使用。 r10此时指向段.proc.info的开始地址。 (3)寄存器r1中的系统类型值(unique architecture number),这个系统类型值的定义,并且由bootloader传入。在文件arch/arm/tools/mach-types中: machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number xhyper250R1 ARCH_PXA_XHYPER250R1 PXA_XHYPER250R1 200 (4)调用lookup_architecture_type,将以r1的值检查.arch.info表,这是个struct machine_desc由文件arch/arm/mach-pxa/xhyper250R1.c中的MACHINE_START()创建。假如没有此结构则提示error:a并挂起。 这里要注意的是,段.arch.info的定位在vmlinux-armv.lds.in文件中紧接.proc.info,这个段定义在include/asm-arm/mach/arch.h文件中,使用了宏定义MACHINE_START()。文件首先定义了一个结构体machine_desc,段.arch.info主体部分使用了宏定义MACHINE_START()其中嵌入这个结构体。 通常来讲MACHINE_START()的实现应该在文件arch/arm/kernel/arch.c中,而这里hyper250的源码中,MACHINE_START()宏定义在arch/arm/mach-pxa/xhyper250R1.c中完成了定义,下面详细分析这个结构: (A)MACHINE_START MACHINE_START(_type,_name)这宏开始处嵌入一个静态结构machine_desc,并且立即声明段.arch.info。 _type是MACH_TYPE(PXA_XHYPER250R1),用以赋值给machine_desc中的nr,这就是系统类型值number(200)。 _name是描述系统类型的字符串,用以赋值给machine_desc中的name为char*。 以下几个宏定义均在包含在machine_desc的赋值中,也在段.arch.info中。 (B)MAINTAINER MAINTAINER(n),这个n并没有赋值给machine_desc结构,n是"Hybus Co,. ltd."字符串,公司名字罢了。 (C)BOOT_MEM BOOT_MEM(_pram,_pio,_vio),这里面很关键,又3个变量: _pram,传值给phys_ram:物理内存的开始地址,程序中赋值为:0xa0000000。 _pio,传值给phys_io:物理io的开始地址,程序中赋值为:0x40000000。 _vio,传值给io_pg_offst:io页表的偏移,程序中赋值为:_vio=0xfc000000,不过要进行转换:((_vio)>>18)&0xfffc=0x3f00 (D)BOOT_PARAMS BOOT_PARAMS(_params)这个宏定义了启动参数页表的偏移:param_offset,程序中赋值为:0xa0000100。 (E)FIXUP(接下来三个宏定义分别是三个函数指针:这些函数都在machine_desc结构中定义并且在xhyper250R1.c中实现。) FIXUP(fixup_xhyper250R1)宏指向fixup_xhyper250R1函数,这个函数有4个参数: fixup_xhyper250R1(struct machine_desc *desc, struct param_struct *params, char **cmdline, struct meminfo *mi) struct machine_desc:这个结构体前面已经提过了。 param_struct:这个结构体定义在include/asm/setup.h中,这是一个向kernel传递参数的结构体。 char **cmdline:好像用于定义输出窗口行数。 struct meminfo:这个结构体定义在include/asm/setup.h中,这是一个对物理内存区间描述的结构体,它将整个地址空间分为8个区间,通常一个区必须是连续的地址并且是同一类型的设备,而用于特殊目的的地址将划分为一个独立的区。首先定义nr_banks:块号,然后是结构体bank[NR_BANKS],NR_BANKS为8。结构体bank[NR_BANKS]中有:start、size、node。 下面分析这个函数fixup_xhyper250R1的工作, 首先,调用宏SET_BANK并赋值为SET_BANK(0, 0xa0000000, 64*1024*1024),这个宏定义在arch/arm/mach-pxa/generic.h文件中。SET_BANK主要完成设置结构体meminfo中bank[_nr]的start、size和node。以上为例,则完成了bank[0]区间中的start=0xa0000000,size=64*1024*1024=64M,node=(__start) - PHYS_OFFSET) >> 27=0 接着,使mi的nr_banks=1,好象设定了这个结构只有一个区。要注意的是meminfo将在page_init()中用于初始化页面。 (F)MAPIO MAPIO(xhyper250R1_map_io)宏指向xhyper250R1_map_io函数,这个函数没有参数,主要用于io地址从虚拟地址到物理地址的映射关系。 这个函数调用了pxa_map_io()和iotable_init(xhyper250R1_io_desc): pxa_map_io()函数定义在arch/arm/mach-pxa/generic.h文件中,实现在arch/arm/mach-pxa/generic.c中,主要调用了iotable_init()函数来进行io地址的区间映象。iotable_init(struct map_desc *)函数中,参数map_desc结构体定义在文件include/asm-arm/map.h中,主要有:virtual、physical、length和一些标志位:domain、read、write、cache、buffer等。iotable_init()函数在文件arch/arm/mm/mm-armv.c中,循环调用create_mapping()来处理map_desc的映射关系。create_mapping函数主要工作就是将io的虚拟地址到物理地址的映射关系按照PAGE_SIZE(4K)的页来进行映射,同时还有段的映射关系。(关于内存的映射将在第2章中详细分析) (G)INITIRQ INITIRQ(xhyper250R1_init_irq)指向了xhyper250R1_init_irq函数,这个函数将主要完成中断的初始化,这里主要调用了函数 pxa_init_irq(),这个函数实现在arch/arm/mach-pxa/irq.c中。接着调用了set_GPIO_IRQ_edge()函数,这个函数也在irq.c中。(关于中断的分析将在以后进行) 我们以上通过分析宏MACHINE_START而分析了结构体machine_desc的一个实例的赋值,我们这里其实只用这个结构体很少一部分信息,主要有三个参数内存物理内存的开始地址、物理io的开始地址、io页表的偏移,分别存于寄存器r5(phys_ram=0xa0000000)、r6(phys_io=0x40000000)、r7(io_pg_offst=0x3f00)中,并返回。 (5)初始化页表,映射了4M的RAM,以使内核运行。 这里值得注意的是:r5此时为物理内存开始地址(0xa0000000),程序利用宏定义pgtbl,将r4成为页表首地址0xC0004000。然后清空内核目录swapper_pg_dir开始的16K空间。 (6)设置lr为返回地址__ret,以使下面的程序得以跳转返回。 (7)使pc=[r10+12],也就是跳转到_xscale_proc_init结构中的b __xscale_setup位置,这个结构在arch/arm/mm/proc-xcale.S中。我们来看看这个结构: __pxa250_proc_info: <--r10指向这个地址 .long 0x69052100 .long 0xfffff7f0 #if CACHE_WRITE_THROUGH .long 0x00000c0a #else .long 0x00000c0e <--这个参数传入了r8中 #endif b __xscale_setup <--[r10+12] .long cpu_arch_name .long cpu_elf_name .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP .long cpu_pxa250_info .long xscale_processor_functions .size __pxa250_proc_info, . - __pxa250_proc_info __xscale_setup相关的程序多是对协处理器cp15的操作,之中用到了宏F_BIT|I_BIT|SVC_MODE, 相关的宏定义在文件include/asm-arm/proc-armv/ptrace.h中。 #define SVC_MODE 0x13 #define T_BIT 0x20 #define F_BIT 0x40 #define I_BIT 0x80 (8)通过proc-xcale.S中__xscale_setup设置MMU,并通过__ret返回head_armv.S。 (9)在__ret返回处设置lr通过__switch_data返回到__mmap_switched。 (10)打开MMU,将pipeline清空,以使所有的内存得以正确的访问。并返回到__mmap_switched。 (11)__mmap_switched通过__switch_data获得数据,并设置了stack pointer。 (12)清空BSS,并保存CPU类型值(processor ID)以及系统类型(machine type)等。 (13)跳转到start_kernel。 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ryman/archive/2005/07/12/422766.aspx
本文档为【(很好的嵌入式linux启动过程分析】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_037932
暂无简介~
格式:doc
大小:55KB
软件:Word
页数:8
分类:互联网
上传时间:2012-07-03
浏览量:18