嵌入式知识点总结 Linux驱动 (二)-uboot bootloader

embedded/2025/1/31 9:50:44/

针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。

目录

1.什么是bootloader?

2.Bootloader的两个阶段

3.uboot启动过程中做了哪些事?

4.uboot和内核kernel如何完成参数传递?

5.为什么要给内核传递参数?

6.如何给内核传递参数?

7.为什么uboot要关掉caches?


1.什么是bootloader?

Linux系统要启动就必须需要一个 bootloader程序,也就说芯片上电以后先运行一段bootloader程序这段 bootloader程序会先初始化时钟,看门狗,中断,SDRAM,等外设,然后将 Linux内核从flash(NAND,NOR FLASH,SD,MMC等)拷贝到SDRAM中,最后启动Linux内核。当然了bootloader的实际工作要复杂的多,但是它最主要的工作就是启动 Linux内核。
bootloader和 Linux内核的关系就跟PC上的BIOS和 Windows的关系一样,bootloader就相当于BIOS。总得来说,Bootloader就是一小段程序,它在系统上电时开始执行,初始化硬件设各、准备好软件环境,最后调用操作系统内核。

loader包含ATF UBOOT (设备树)FDT
对于复杂系统系统启动流程一般是:BootRom、Bootloader、Kernel、Filesystem(文件系统)、App
BootLoader的主要功能包括:关闭看门狗,初始化中断和异常向量表,进行时钟和外设的初始化,提供Can、Uart、Flash读写驱动等等。 大多数的Bootloader分为两个阶段,第一阶段使用汇编来实现,它完成一些依赖于CPU体系结构的初始化并调用第二阶段的代码;第二阶段则通常使用C语言来实现,这样可以实现更复杂的功能,而且代码会有更好的可读性和可移植性。 SPL(Secondary Program Loader)。硬件设备初始化、初始化内存空间、初始化堆栈,随后将第二阶段的代码(uboot)复制到SRAM中,跳转到Uboot入口地址处Uboot第二阶段初始化本阶段要使用到的硬件设备,通常会初始化一个串口做命令行方便交互。随后可能检测系统内存映射(memory map),并将内核代码复制到DDR中,最后准备传递给内核的参数,并引导内核

2.Bootloader的两个阶段

(1)Bootloader第一阶段的功能 硬件设备初始化 为加载Bootloader 的第二阶段代码准备RAM空间 复制 Bootloader 的第二阶段代码到RAM空间中 设置好栈 跳转到第二阶段代码的C入口点
(2)Bootloader第二阶段的功能 初始化本阶段要使用到的硬件设备(如串口、Flash和网卡等) 检测系统内存映射( memory map ) 将内核映象和根文件系统映象从Flash 上读到RAM空间中 ·为内核设置启动参数 调用内核

3.uboot启动过程中做了哪些事?

硬件上电或复位。

执行 SPL(若存在),初始化基本硬件。

加载主 U-Boot 到 RAM 并执行。

初始化硬件和外设。

加载环境变量。

检测和选择启动设备。

加载内核和根文件系统。

跳转到内核,完成启动过程。

4.uboot和内核kernel如何完成参数传递?

U-Boot和Linux内核之间的参数传递通常通过环境变量设备树来实现。以下是几种常见的参数传递方式:

1. 通过 U-Boot 环境变量传递参数

U-Boot 提供了一个环境变量机制,可以在启动时将一些参数传递给内核。这些环境变量通常用于传递系统配置、启动选项等。

在 U-Boot 中设置环境变量:

setenv bootargs "root=/dev/mmcblk0p2 console=ttyS0,115200"
saveenv

在启动内核时,U-Boot 会将这些环境变量传递给内核,通常是通过 bootargs 参数。内核启动时可以通过 bootargs 来获取启动时传递的参数。

U-Boot 中的环境变量可以通过 printenv 命令查看,修改后使用 saveenv 保存。

内核启动时,会通过 bootargs 参数接收到这些传递的参数。内核可以通过 getenv()parse_bootargs() 函数来解析这些参数。

2. 通过设备树传递参数

设备树(Device Tree,DT)是一种硬件描述的标准格式,内核在启动时会加载设备树以获取硬件配置信息。你也可以通过设备树来传递一些启动时的配置参数。

在设备树文件中,你可以定义一个 chosen 节点,用于传递启动参数。例如:

/ {chosen {bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2";};
};

在内核启动时,内核会解析设备树中的 bootargs 参数,通常它会覆盖 U-Boot 传递的 bootargs 环境变量,或者可以与之合并。

设备树中的参数可以通过 of_get_flat_dt_prop()of_get_property() 函数读取。在内核代码中,通常会在初始化阶段读取这些信息。

有时,我们还会用到 U-Boot 的 bootcmd 命令来启动内核,这时可以在命令行中指定内核参数。

例如,在 U-Boot 中通过以下命令启动内核:

run bootcmd

bootcmd 脚本中,你可以设置 bootargs,然后使用 bootmbootz 等命令启动内核。

5.为什么要给内核传递参数?

在此之前,uboot已经完成了硬件的初始化,可以说已经"适应了“这块开发板。然而,内核并不是对于所有的开发板都能完美适配的(如果适配了,可想而知这个内核有多庞大,又或者有新技术发明了,可以完美的适配各种开发板),此时,对于开发板的环境一无所知。所以,要想启动Linux内核,uboot必须要给内核传递一些必要的信息来告诉内核当前所处的环境

给内核传递参数是为了在系统启动时调整内核的行为或配置运行环境。这些参数会影响内核的初始化过程、硬件配置和功能启用。

参数作用
console=ttyS0,115200指定串口为控制台,设置波特率为 115200。
root=/dev/mmcblk0p1指定根文件系统所在设备。
rootfstype=ext4指定根文件系统的类型为 ext4。
quiet禁止内核启动时打印日志信息。
loglevel=7设置内核日志打印的详细级别。
mem=512M限制可用内存为 512 MB。
nohz=on启用动态时钟。
maxcpus=1限制系统只使用一个 CPU 核心。
panic=10系统崩溃后 10 秒重启。

6.如何给内核传递参数?

U-Boot 是嵌入式系统的常用引导加载程序,它支持通过环境变量传递参数给内核,特别是 bootargs 变量。

设置环境变量: 在 U-Boot 提示符下,设置 bootargs 环境变量,这个变量通常包含内核启动时的参数。

setenv bootargs "console=ttyS0,115200 root=/dev/mmcblk0p2 rw"

保存环境变量: 设置完成后,使用 saveenv 命令保存这些环境变量:

saveenv

启动内核: 启动内核时,U-Boot 会将 bootargs 环境变量传递给内核。例如,可以使用 bootmbootz 命令启动内核:

bootm 0x80008000

内核获取参数: 内核会从 U-Boot 传递过来的 bootargs 中读取启动参数。内核启动时会将这些参数存储在 boot_args 中,你可以在内核代码中通过 getenv()parse_bootargs() 等函数解析这些参数。

如果你在 U-Boot 中设置了 bootargs="console=ttyS0,115200 root=/dev/mmcblk0p2",内核启动时就会使用这些参数进行初始化,例如设置串口终端(console)和挂载根文件系统(root=/dev/mmcblk0p2)。

2. 通过设备树传递参数

设备树(Device Tree,DT)是描述硬件的一种标准方式,内核会读取设备树来配置硬件资源。有时你也可以在设备树中指定一些参数,尤其是与硬件配置相关的启动参数。

3. 通过内核启动命令行参数

有时,启动命令本身也可以直接在启动脚本中传递给内核。例如,如果你的引导加载器(如 GRUB)支持,你可以直接在启动命令中指定内核启动参数。

在启动命令中指定参数: 在引导加载器的配置文件中(如 GRUB 的 grub.cfg),你可以为内核指定启动参数:

linux /vmlinuz-5.10.0 root=/dev/sda1 console=ttyS0,115200

内核获取参数: 内核启动时会从引导加载器传递的命令行中获取这些参数。

4. 通过内核命令行

内核启动时,通常会使用命令行参数(bootargs)。这些参数在内核启动时由引导加载器(如 U-Boot、GRUB 等)传递。内核可以通过 command_line 全局变量或 getopt() 函数解析这些参数。

常用的内核命令行参数:

  • root=<root_device>:指定根文件系统。
  • console=<console_device>:指定控制台设备。
  • mem=<size>:指定内存大小。
  • debug:启用调试模式。

7.为什么uboot要关掉caches?

caches是cpu内部的一个2级缓存,它的作用是将常用的数据和指令放在cpu内部。caches是通过CP15管理的,刚上电的时候,cpu还不能管理caches。上电的时候指令cache可关闭,也可不关闭,但数据cache一定要关闭,否则可能导致刚开始的代码里面,去取数据的时候,从cache里面取,而这时候RAM中数据还没有caches过来,导致数据预取异常


http://www.ppmy.cn/embedded/158318.html

相关文章

Vuex中的getter和mutation有什么区别

在现代前端开发中&#xff0c;状态管理是一个不可忽视的话题&#xff0c;而Vuex作为Vue.js的官方状态管理库&#xff0c;在大型应用中扮演着至关重要的角色。当我们使用Vuex进行状态管理时&#xff0c;getter和mutation是两个重要的概念。虽然它们都是用来处理状态的&#xff0…

大厂面试题备份20250129

20250129 KV CACHE压缩 在大语言模型推理中&#xff0c;KV Cache&#xff08;Key-Value Cache&#xff09;用于存储过去的注意力键&#xff08;Key&#xff09;和值&#xff08;Value&#xff09;&#xff0c;加速自回归解码。然而&#xff0c;KV Cache会随输入长度增加而占用…

K8S中高级存储之PV和PVC

高级存储 PV和PVC 由于kubernetes支持的存储系统有很多&#xff0c;要求客户全都掌握&#xff0c;显然不现实。为了能够屏蔽底层存储实现的细节&#xff0c;方便用户使用&#xff0c; kubernetes引入PV和PVC两种资源对象。 PV&#xff08;Persistent Volume&#xff09; PV是…

【Leetcode 每日一题】119. 杨辉三角 II

问题背景 给定一个非负索引 r o w I n d e x rowIndex rowIndex&#xff0c;返回「杨辉三角」的第 r o w I n d e x rowIndex rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 数据约束 0 ≤ r o w I n d e x ≤ 33 0 \le rowIndex \le 33 …

【由浅入深认识Maven】第3部分 maven多模块管理

文章目录 第三篇&#xff1a;Maven多模块管理一、前言二. 多模块项目结构1、多模块项目的典型结构2、父POM与子模块POM的关系3、子模块POM配置 三、 多模块项目的构建四、 版本管理与模块间依赖五、 总结 第三篇&#xff1a;Maven多模块管理 一、前言 开发大型项目时&#xf…

7.抽象工厂(Abstract Factory)

抽象工厂与工厂方法极其类似&#xff0c;都是绕开new的&#xff0c;但是有些许不同。 动机 在软件系统中&#xff0c;经常面临着“一系列相互依赖的对象”的创建工作&#xff1b;同时&#xff0c;由于需求的变化&#xff0c;往往存在更多系列对象的创建工作。 假设案例 假设…

2025最新版MySQL安装使用指南

2025最新版MySQL安装使用指南 The Installation and Usage Guide of the Latest Version of Oracle MySQL in 2025 By JacksonML 1. 获取MySQL 打开Chrome浏览器&#xff0c;访问官网链接&#xff1a;https://www.mysql.com/ &#xff0c;随即打开MySQL官网主页面&#xff…

Springboot使用AOP时,需不需要引入AspectJ?

Springboot使用AOP时,需不需要引入AspectJ? 在Spring Boot中使用AOP时&#xff0c;是否需要引入AspectJ取决于你选择的具体AOP实现方式。以下是详细分步说明&#xff1a; 1. 默认场景&#xff1a;使用Spring AOP&#xff08;基于代理&#xff09; 不需要引入AspectJ依赖&am…