关于嵌入式系统的启动
(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
fo
分享到:
相关推荐
ARM嵌入式系统启动过程分析及实现 比较详细
嵌入式Linux系统的启动(很好的嵌入式linux启动过程分析)
嵌入式操作系统启动代码的分析 适用于WINCE,LINUX
嵌入式系统的内核引导启动过程浅析 嵌入式系统的内核引导启动过程浅析 嵌入式系统的内核引导启动过程浅析 嵌入式系统的内核引导启动过程浅析 嵌入式系统的内核引导启动过程浅析
介绍了ARM嵌入式处理系统的使用现状,以及各大嵌入式操作系统的特点,结合常用的嵌入式系统的开发,提出了一种基于ARM- VxWorks 嵌入式系统的启动引导方案,该方案可以通过简易串口方便地更新嵌入式系统内的软件平台,并...
介绍嵌入式系统开机启动过程
我们在这里讨论的是对嵌入式 linux 系统的启动过程的输出信息的注释,通过我们的讨论,大家会对嵌入式 linux 启动过程中出现的、以前感觉熟悉的、但却又似是而非的东西有一个确切的了解,并且能了解到这些输出信息的...
随着物联网(IoT)装置的广泛普及——从智慧城市到无线珠宝,物联网几乎渗透到日常生活的每一步,对于物联网类型的...确保安全的启动过程是保护任何嵌入式系统的首要步骤,也是在应用中预防恶意软体壁垒的必要部份。
目前,嵌入式系统的硬件平台和应用方向区别很大,但总体启动流程一致的。这里的系统启动是指从用户执行上电/复位操作,到系统开始提供用户可接收的服务水平所需要的过程。典型的上电/复位时序如表1所列。 表1 ...
3.4.3 系统启动及初始化相关文件 91 3.4.4 芯片初始化、主程序、中断程序及其他文件 98 3.4.5 机器码文件(s19文件)的简明解释 101 3.4.6 lst文件与map文件 103 3.4.7 如何在CW环境下新建一个S12工程 105 3.5...
我们在这里讨论的是对嵌入式linux系统的启动过程的输出信息的注释,通过我们的讨论,大家会对嵌入式linux启动过程中出现的、以前感觉熟悉的、但却又似是而非的东西有一个确切的了解,并且能了解到这些输出信息的...
关于嵌入式开发ARM系统的启动引导程序代码
基于ARM的嵌入式系统启动优化,韩平,,本文介绍了基于ARM的嵌入式系统的相关启动的优化问题,结合软硬件,提供了更为高效,更为低成本的系统优化方案。应用三星公司的ste
ARM嵌入式系统设计基础教程课件 第1章介绍了嵌入式系统的定义和组成、嵌入式微处理器体系结构和类型。 第2章介绍了ARM微处理器结构、寄存器结构、异常处理、存储器结构、指令系统和接口。 第3章介绍了S3C2410A的...
嵌入式系统内核启动模块的设计与实现
很全面的Linux嵌入式系统环境搭建说明文档,其中详细介绍了内核配置、最小系统启动以及设备文件分析
加快嵌入式Linux系统启动速度的方法及应用.pdf
上海双实嵌入式系统开发教程—基于ARM的嵌入式系统设计与实现// ....基于ARM的嵌入式系统设计与实现(目录).pdf ....第10讲 嵌入式系统的优化.pdf ....第1讲 嵌入式系统基础.pdf ....第2讲 基于ARM的板级嵌入式...
嵌入式Linux 系统启动盘制作,包含详细的步骤以及代码
ARM7嵌入式系统教程 第2章 ARM指令系统及运算基础实验 10 2.1 二进制数加减运算 10 2.2 带符号数的加减法运算 12 2.3 简单分支程序设计 15 2.4 数据块转储实验(循环程序设计) 16 2.5 数值1到数值100的累加程序设计...