arm环境u-boot功能和linux启动过程

1. *U-BOOT概况总结*

一、 启动代码为start.S

二、 由于u-boot为裸板上第一个程序,所以要初始化各个外设,由于u-boot已经初始化了,

包括:中断禁用、分配动态内存、初始化BBS区域、初始化页目录、打开缓存等任务。

所以linux内核不需要全部再次初始化,所以可以理解bootloader和kernel其实是两兄弟。

例:

/start code on reset/

Reset:

​ /初始化cpu/

bl set_cpu_mode /设置特权模式/

/初始化中断和时钟外设/

bl turn_off_watchdog /关闭看门狗/

bl mask_irqs /关闭中断/

bl set_clock /设置时钟/

/初始化内存/

bl disable_id_caches /关闭caches/

bl init_memory /初始化sram/

bl init_stack /初始化栈/

bl clean_bss /初始化bss/

​ /初始化硬盘/

bl nand_init /初始化nand/

​ /应用/

bl copy_to_ram /复制自身到ram/

​ ldr pc, =arm_main /执行main函数/

三、 U-boot引导内核第一步要做的的是把内核下到ram中,然后跳到内核的start函数。

四、bootloader必须提供5种功能:RAM初始化、串行端口初始化、查找机器类别、构建tagged list内核、将控制移交到内核镜像。

2. *linux内核初始化流程*

img

u-boot跳到linux内核的arch/arm/boot/compressed/head.S中start后

img

内核大致可以理解为三部曲:

第一步 解压内核并跳到start_kernel()。

​ 1.arch/arm/boot/compressed/head.S(start)

准备解压内核

​ 2.arch/arm/boot/compressed/head.S

通过decompress_kernel 解压zImage

通过call_kernel调用已解压后内核vmlinux

​ 3.arch/arm/kernel/head.S

通过ENTRY(stext)->

处理器信息搜寻——__look_processor_type

搜寻我的机型——__lookup_machine_type

通过__mmap_switched调用start_kernel

第二步 跳到真正第一个内核代码init/main.c(start_kernel())。 注:在linux 0.11中为init/main.c main()。

其中明星函数(排名不分先后):

1.初始化console(此处为平台相关需要hack的地方<1>)

start_kernel->console_init->serial_pxa_console_init

2.注册SOC初始化函数machine_desc

start_kernel->setup_arch()->setup_machine(machine_arch_type)[/arch/arm/kernel/setup.c]

img

[/arch/arm/mach-s3c2440/mach-mini244.c]

MACHINE_START(MINI2440, “FriendlyARM Mini2440 development board”)

​ .phys_io = S3C2410_PA_UART,

​ .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

​ .boot_params = S3C2410_SDRAM_PA + 0x100,

​ .init_irq = s3c24xx_init_irq,

​ .map_io = mini2440_map_io,

​ .init_machine = mini2440_machine_init,

​ .timer = &s3c24xx_timer,

MACHINE_END

1)init_machine 在start_kernel()->setup_arch()->do_initcalls()->customize_machine()->mini6410_machine_init()

2)init_irq在start_kernel() -> init_IRQ() -> init_arch_irq()中被调用

3)map_io 在 setup_arch() -> paging_init() -> devicemaps_init()中被调用

4)timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。

在start_kernel() -> time_init()中被调用。

5)boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。

http://blog.csdn.net/boyemachao/article/details/45394363

3.初始化虚拟驱动平台platform

start_kernel->kernel_init()->do_basic_setup()->driver_init()->platform_bus_init()→bus_register(&platform_bus_type)

4.初始化SOC(initcall参考下面的init.h)

start_kernel->kernel_init()->do_basic_setup()->do_initcalls()->do_one_initcall()

img

第三步 就为愉快的init的代码了也就是第一个进程,在linux 0.11中集成在内核中。在之后版本由用户态提供。

​ 当然我已经升级为systemd啦,哈哈。

3. *linux初始化代码框架*

哪部分是只和CPU相关的部分,

哪部分是SOC平台相关的部分,

也再次标明LINUX下大致逻辑。

第一部分代码arch/arm/boot/

简介:也就是自我解压部分,可以理解为和cpu相关但和SOC无关。

移植:移植时选择对应的cpu。

功能:arch/arm/boot/compressed/head.S自我解压前准备、解压缩。跳到start_kernel。

第二部分代码init/main.c

简介:也就是start_kernel()或0.11中的main() 可以理解内核真正的第一代码。为平台无关代码。

移植:移植时不用管。

功能:此代码虽说简单的大几十行,但功能太多,我还是给个linux 0.11中main()的靓照吧_

img

第三部分代码 arch/arm/PLAT- arch/arm/MACH-

​ 简介:为了完成start_kernel()中初始化的SOC相关部分。

移植:移植时需要考虑对应的SOC。没有对应soc支持时,才是各路玩家大显身手的时候。

功能:看下面的介绍吧。

4. *Mini2440的plat和mach*

plat-s3c24xx

mach-s3c2440

mach-s3c2410

======================

\1. 三星这样分层的理由是s3c系列的soc具有一定的共通性, plat-实现了一些较通用的封装, 这些封装的具体参数一般是宏, 这些宏如寄存器地址可能是在mach-里面被定义;

linux/arch/arm/plat-s3c24xx/common-smdk.c

​ static struct s3c24xx_led_platdata smdk_pdata_led5 = {

​ .gpio = S3C2410_GPF5,

​ .flags = S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,

​ .name = “led5”,

​ .def_trigger = “nand-disk”,

​ };

linux/include/asm-arm/arch-s3c2410/regs-gpio.h

​ #define S3C2410_GPF5 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 5)

\2. 原则上是把所有s3c系列共同的东西放在 plat-里面去, 具体的io或者比较有mach-特色的部分放到mach-里面;

改板时, 实际上大多是直接在mach-里面增删自己的功能. (不按三星预设方案的改动除外)

plat里面需要动的相对更少, 不过在linux/arch/arm/plat-s3c24xx/common-smdk.c里面,

我们可以根据实际情形来分配nand的分区(修改static struct mtd_partition smdk_default_nand_part[] );

\3. 编译时,一般只会选中一个特定的mach-, mach-会调用plat-的功能具体实现平台的资源和设备初始化.

*5.内核移植*

在一般普通的产品公司,大部分都是在soc上添加自己需要的外设做成产品。

并且常见的soc都已经有了成熟的soc代码支持。

现在讨论内核移植重点在板级支持,这属于BSP工程师的范围。并非属于芯片工程。

BSP工程师的能力就是要在各种板子上移植我们万众期待的linux内核。

总的来说,我们所做的就是要找到外设的区别,熟悉内核的框架,

修改外设的驱动,使内核完美的运行起来就好了。

所涉及到的驱动:GPIO、协处理器、中断、时钟、MMU、存储控制器、NAND、UART、IIC等。

我们可以找出最基本的,启动linux所依赖的。并根据依赖的顺序一一攻破。

我暂时认为,学习此部分好的方法是先在一个成熟板子上写一个裸板程序。

这样可以熟悉板子硬件,也可熟悉一个小的系统是如何跑起来的。

有了这个基础我们就能把我哪些是最为基础的驱动。和依赖的顺序等。

通过本文可以体会到,对于内核,移植它改造它有难度呀。

会裸板驱动、会写bootloader只是基础。

还要熟悉内核代码,真是上知系统、应用,下知硬件、驱动。

参考资料:

[console] early printk实现流程

http://blog.csdn.net/ooonebook/article/details/52654120

linux2.6中的console_init初始化的研究

http://blog.csdn.net/breeze_vickie/article/details/5563375

linux下tty,控制台,虚拟终端,串口,console(控制台终端)详解

http://blog.csdn.net/liaoxinmeng/article/details/5004743

【原创】s3c2440内核启动时如何注册串口为终端设备

http://blog.sina.com.cn/s/blog_70ef2ee90100zc4z.html


arm环境u-boot功能和linux启动过程
http://blog.uanet.cn/KERNEL/arm环境u-boot功能和linux启动过程.html
作者
dnsnat
发布于
2023年1月22日
许可协议