`
isiqi
  • 浏览: 16561581 次
  • 性别: Icon_minigender_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 的编程标准书写的汇编文件初始化了系统相关的硬件 , 并且完成了 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 没有 压缩过的内核。 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) <span sty
分享到:
评论

相关推荐

    ARM嵌入式系统启动过程分析及实现

    ARM嵌入式系统启动过程分析及实现 比较详细

    嵌入式Linux系统的启动(很好的嵌入式linux启动过程分析)

    嵌入式设备通常有一个固件,如BIOS(基本输入输出系统)或U-Boot(统一启动加载器),它们是系统启动的第一步。在嵌入式环境中,由于资源限制,U-Boot更为常见,它是一个轻量级的引导加载器,负责初始化CPU、内存、...

    嵌入式系统的内核引导启动过程浅析

    最后是根文件系统,包含了系统启动所需的文件和目录。 #### Bootloader的操作模式 大多数Bootloader具备两种主要的操作模式: 1. **启动加载模式**:此模式下,Bootloader自动从固态存储设备加载操作系统到RAM中...

    嵌入式系统开机启动介绍

    ### 嵌入式系统开机启动介绍 #### 1. 嵌入式系统概述 - **定义**:根据IEEE的定义,嵌入式系统是一种用于控制、监控或辅助装置、机器和设备运行的装置。在国内,普遍认可的定义则是以应用为中心、以计算机技术为...

    基于strongARM-VxWorks 嵌入式系统启动引导的实现

    介绍了ARM嵌入式处理系统的使用现状,以及各大嵌入式操作系统的特点,结合常用的嵌入式系统的开发,提出了一种基于ARM- VxWorks 嵌入式系统的启动引导方案,该方案可以通过简易串口方便地更新嵌入式系统内的软件平台,并...

    嵌入式系统原理及应用 马维华

    第七章详细探讨了嵌入式系统Boot Loader,Boot Loader是嵌入式系统启动时运行的第一段代码,负责初始化硬件设备、建立内存空间映射,从而为操作系统及其他软件的加载和运行做好准备。 第八章重点讲解了嵌入式系统...

    经典—详解嵌入式linux启动信息_经典

    嵌入式系统中,根文件系统可能是RAMDisk、NFS网络文件系统或者嵌入到内核中的ramfs。一旦根文件系统被挂载,系统就能执行/sbin/init,这是系统启动的第一个用户级进程。 在init进程的管理下,系统会按照特定的运行...

    嵌入式系统实现安全启动

    嵌入式系统实现安全启动是物联网(IoT)设备安全防护的重要环节,尤其在智能城市、无线珠宝等广泛应用背景下,确保设备的安全启动至关重要。安全启动是一种机制,它要求在操作系统(OS)启动之前,硬件必须对启动镜像和...

    嵌入式系统设计师真题

    在固件层面,Bootloader是嵌入式系统启动过程中的关键软件,负责加载操作系统到内存中。固件还需要考虑到设备初始化、硬件驱动程序的编写以及低级功能的实现。 操作系统(OS)是嵌入式系统的核心组成部分,常见的有...

    详解嵌入式linux启动信息

    - 网络服务如DHCP、DNS、网络接口配置等在系统启动过程中进行,确保网络连接可用。 8. **系统日志记录** - syslog服务启动,记录系统启动过程中的事件和错误信息。 9. **守护进程启动** - 各种后台服务(守护...

    嵌入式系统介绍-3

    最后,17600嵌入式系统4-嵌入式系统设计U-BOOT.pdf和17600嵌入式系统4-嵌入式系统设计-MP3播放.pdf可能分别涉及到启动加载器(如U-Boot)的配置和应用,以及多媒体处理的实例,如MP3播放功能的实现。这些具体案例...

    详解嵌入式linux启动信息.pdf

    嵌入式Linux系统的启动流程是复杂而精细的,它涉及到多个层次的软件和硬件交互。在启动过程中,系统会输出大量的信息,这些信息对于开发者来说...对于开发者而言,理解这些启动信息有助于调试、优化和维护嵌入式系统。

    基于ARMuCLinux嵌入式系统启动引导的实现

    ### 基于ARM-uCLinux嵌入式系统启动引导的实现 #### 摘要: 本文探讨了ARM-uCLinux嵌入式系统的结构组成及其启动引导过程的关键技术和实现方案。ARM-uCLinux嵌入式系统因其高性能和低功耗的特点,在消费电子、无线...

    32位ARM嵌入式系统PPT

    BootLoader是系统启动的关键,它负责初始化硬件,设置内存映射,以及创建一个适合运行应用程序的环境。BootLoader是硬件和软件间的桥梁,对于32位ARM系统,S3C4510B的编址空间映射是BootLoader开发中必须理解的部分...

    ARM嵌入式系统基础教程习题答案

    * 嵌入式操作系统是一段在嵌入式系统启动后首先执行的背景程序。 * 嵌入式操作系统的优点包括提高系统的可靠性、提高开发效率、缩短开发周期、充分发挥 32 位 CPU 的多任务潜力等。 四、项目开发生命周期 * 项目...

    2018年下半年嵌入式系统设计师考试嵌入式系统基础知识真题.doc

    2. **BIOS**:基本输入输出系统(BIOS)是保存在主板上的只读存储器(ROM)中,负责初始化硬件设备和提供低级服务,支持操作系统启动。 3. **数据表示与编码**:在n位补码表示法中,包括一个符号位,可以直接表示的...

    嵌入式系统期末考试题1.pdf

    Boot Loader 是在嵌入式系统复位启动时,操作系统内核运行前,执行的一段程序。通过 Boot Loader,初始化硬件设备,建立内存和I/O 空间映射图,为最终加载操作系统内核调整好适当的系统软硬件环境。 知识点12:...

    嵌入式系统引导介质简便切换的启动方法设计

    关于系统启动方法,嵌入式设备和我们熟知的个人电脑一样,支持从不同的设备启动。常见的启动介质有硬盘、DVD、U盘、eMMC存储器、SD卡、USB接口和以太网接口等。在嵌入式系统中,启动介质的选择对于系统的初始化和...

Global site tag (gtag.js) - Google Analytics