当前位置:首页 > 嵌入式 > 嵌入式硬件
[导读]通用的linux内核,启动时需要很多参数 ,这些参数必须通过Bootloader传递。而且内核一半是压缩存放在外存上的,从外存到内存的复制也是由Bootloader完成。从Bootloader的第二个功能就知道,Bootloader时不能与内核放在一起的。由于Bootloader的实现依赖于CPU的体系结构,因此大多数的Bootloader都分为Stage1和Stage2l两大部分

通用的linux内核,启动时需要很多参数 ,这些参数必须通过Bootloader传递。而且内核一半是压缩存放在外存上的,从外存到内存的复制也是由Bootloader完成。从Bootloader的第二个功能就知道,Bootloader时不能与内核放在一起的。由于Bootloader的实现依赖于CPU的体系结构,因此大多数的Bootloader都分为Stage1和Stage2l辆大部分。

依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在Stage1中,而且通常用汇编语言来实现,以达到短小精悍的目的。而Stage2则通常用C语言来实现,这样可以实现复杂的功能,并且代码会具有更好的可读性和可移植性。

(1)Bootloader的Stage1通常包括以下步骤:

1:硬件设备初始化

2:为加载Bootloader的Stage2准备RAM空间

3:将Bootoader的Stage2复制到RAM空间中

4:设置好堆栈

5:跳转到Stage2的C入口点

(2)Bootloader的Stage2通常包括以下步骤:

1:初始化本阶段要使用到的硬件设备

2:检测系统内存映射(Memory Map)

3:将Kernel映像和根文件系统映像从Flash上读到RAM空间中

4:为内核设置启动参数

5:调用内核。

Stage1:汇编阶段

1:基本硬件初始化

(1)屏蔽所有中断。

(2)设置CPU的速度和时钟频率。

(3)RAM初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存控制寄存器等。

(4)初始化LED。(或者初始化串口,用于表示当前状态)

(5)关闭CPU内部指令/数据Cache。

2:为加载Stage2准备RAM空间

为了获得更快的执行速度,通常把Stage2加载到RAM空间中来执行**,因此必须为加载Bootloader的Stage2准备好一段可用的RAM空间范围。主要工作是对预备空间进行读写测试。

3:复制Stage2到RAM中

复制时注意两点:Stage2的可执行映像在固态存储设备的存放起始位地址和终止地址。RAM空间的起始地址。

4:设置堆栈指针sp

堆栈指针设置是为执行C语言代码做准备。通常可把sp的值设置为(stage2_end-4),也即1MB的RAM空间的最顶端(堆栈向下生长)。此外,在设置堆栈指针sp之前,也可以关闭LED灯,以提示用户我们准备跳转到Stage2。经过上述这些执行步骤后,系统的内存物理布局如下图所示。

5:跳转到Stage2的C入口点

在上述一切都就绪后,就可以跳转到Bootloader的Stage2去执行了。比如,在ARM系统中,这可以通过修改PC寄存器为合适的地址来实现。

Stage2:C语言阶段

正如之前所说,Stage2代码通常用C语言来实现,以便于实现更加复杂的功能和取得更好的代码可读性和可移植性。但是与通常C语言应用程序不同的是,在编译和链接Bootloader这样的程序时,我们不能使用glibc库中国年的任何支持函数。原因显而易见。

这就给我们带来一个问题,那就是从哪里跳转进main()函数呢?直接把main()函数的起始地址作为整个Stage2执行映像的入口点或许是最直接的想法。但是这样做有两个缺点:

- 无法通过mian()函数传递函数参数

- 无法处理main()函数的返回值情况

一种更为巧妙的方法是利用Trampoline(弹簧床)的概念。也即,用汇编语言写一段Trampoline小程序,并将这段小程序作为Stage2可执行映像的执行入口点。然后我们可以在Trampoline汇编小程序中用CPU跳转指令跳入main()函数中去执行;而当main()函数返回时,CPU执行路径显然再次回到我们的Trampoline程序。简而言之,这种方法的思想就是用这段Trampoline小程序最为mian()函数的外部包裹(External Wrapper)。

下面给出一个简单的Trampoline程序示例(来自Blob):

.text

.globl _trampoline

_trampoline:

bl main

/*if main ever returens we just call it again */

b _trampoline

可以看到,当main()函数返回之后,我们又重新调用trampoline,也就相当于循环调用mian(),这就是弹簧床的概念。

1:初始化系统本阶段需要用到的硬件

需要初始化的设备包括:

- 至少一个串口,以便于终端用户进行I/O信息输出

- 计时器等

2:检测系统的内存映射(Memory Map)

所谓内存 映射,就是指在整个4GB物理地址空间中有哪些地址范围被分配用来寻址系统的RAM单元。比如,在Samsung S3C44B0X中,从0x0c000000到0x10000000之间的64MB地址空间被用作系统的RAM地址空间。

虽然CPU通常预留出一大段足够的地址空间给系统RAM,但是在搭建具体的嵌入式系统时却不一定会实现CPU预留的全部RAM地址空间。也就是说,具体的嵌入式系统往往只把CPU预留的全部RAM空间的一部分映射到RAM单元上,而让剩下的那部分预留RAM地址空间处于未使用状态,不挂满可节省成本。由于上述事实,因此Bootloader的Stage2必须在它想干点什么(比如,将存储在Flash上的内核映像独到RAM空间中)之前检测整个系统的内存映射情况,也即它必须知道CPU预留的全部RAM地址空间中的哪些呗真正映射到物理RAM地址单元,哪些是处于unused状态的。

3:加载内核映像和根文件系统映像

规划内存占用的布局。这里面就包括两个方面:

-内核映像所占用的内存范围。

-根文件系统所占用的内存范围。在规划内存范围占用的布局时,主要考虑基地址和映像的大小两个方面。

对于内核映像,一般将其复制到从(MEM_START + 0x8000)这个地址开始的大约1MB大小的内存范围内(嵌入式LINUX的内核一般都不超过1MB)。

为什么把从MEM_START到MEM_START + 0x8000这段32KB大小的内存空出来呢?这是因为LINUX内核要在这段内存中放置一些全局数据结构,如启动参数和内核页表等信息。

而对于根文件系统映像,则一般将其复制到MEM_START + 0x0010 0000开始的地方。如果用Ramdisk作为根文件系统映像,则其解压后的大小一般是1MB。

4:从Flash上复制

由于像ARM这样的嵌入式CPU通常都是在同一的内存地址空间中国年寻址Flash等固态存储设备的,因此从Flash上读取数据与从RAM单元中读取数据并没有什么不同。用一个简单的循环就可以完成从Flash设备上复制映像的工作:

while(count){

*dest++ = *src++; /*they are all aligned with word boundary */

count -= 4; /* byte number */

};

5:设置内核的启动参数

应该说,在讲内核映像和根文件系统映像复制到RAM空间中后,就可以准备启动Linux内核了。但是在调用内核之前,应该做一步准备工作,即设置Linux内核的启动参数。

Linux 2.4以后的内核期望以标记列表(Tagged List)的形式来传递启动参数。启动参数标记列表以标记ATAG_CORE开始,以标记ATAG_NONE结束。每个标记由北传递参数tag_header结构以及随后的参数值数据结构来组成。数据结构tag和tag_header定义在Linux内核源码的include/asm/setup.h头文件中。

在嵌入式Linux系统中,通常要由Bootloader设置的常用启动参数有ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。

Linux内核在启动时可以以命令行参数的形式来接受信息,利用这一点我们可以向内核提供哪些内核不能自己检测的硬件参数信息,活着重载(override)内核自己检测到的信息。比如,我们用这样一条命令行参数字符串“console=ttyS0,115200n8”来通知内核以ttyS0作为控制台,且用串口采用“115200bps,无奇偶校验、8位数据位”这样的设置。

6:掉用内核

Bootloader调用Linux内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到MEM_START + 0x8000地址处。在跳转时要满足下列条件。

- CPU寄存器的设置:R0=0; R1=机器类型ID; R2=启动参数标记列表在RAM中的起始基地址。

- CPU模式:必须禁止中断(IRQs和FIQs);CPU必须处于SVC模式。

- Cache和MMU的设置:MMU必须关闭;指令缓存可以打开也可以关闭;数据缓存必须关闭。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭