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

news/2025/1/31 12:07:40 标签: linux, 服务器, arm开发, 物联网, mcu, 单片机, 嵌入式硬件

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

目录

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.niftyadmin.cn/n/5838647.html

相关文章

docker安装nacos2.2.4详解(含:nacos容器启动参数、环境变量、常见问题整理)

一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull nacos:2.2.4 2、离线包下载 两种方式&#xff1a; 方式一&#xff1a; -&#xff09;在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -&#xff09;导出 # 导出镜像到…

如何有效利用数据采集HTTP代理

数据采集HTTP代理是一种有效的工具&#xff0c;能够帮助用户在进行数据采集时提升效率。它通过将请求发送到目标服务器的方式&#xff0c;能够有效地隐藏用户的真实IP地址&#xff0c;从而实现更加安全和高效的数据获取。随着信息时代的发展&#xff0c;数据采集HTTP代理的应用…

自定义数据集使用scikit-learn中的包实现线性回归方法对其进行拟合

一、导入必要的库 import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score 二、加载自定义数据集 # 创建自定义数据集 # 假设我们有一个…

Java基础知识总结(三十二)--API--- java.lang.Runtime

类中没有构造方法&#xff0c;不能创建对象。 但是有非静态方法。说明该类中应该定义好了对象&#xff0c;并可以通过一个static方法获取这个对象。用这个对象来调用非静态方法。这个方法就是 static Runtime getRuntime(); 这个Runtime其实使用单例设计模式进行设计。 class …

基于Springboot的社区药房管理系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业多年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的设计程序开发&#xff0c;开发过上千套设计程序&#xff0c;没有什么华丽的语言&#xff0c;只有实…

Python NumPy(8):NumPy 位运算、NumPy 字符串函数

1 NumPy 位运算 位运算是一种在二进制数字的位级别上进行操作的一类运算&#xff0c;它们直接操作二进制数字的各个位&#xff0c;而不考虑数字的整体值。NumPy 提供了一系列位运算函数&#xff0c;允许对数组中的元素进行逐位操作&#xff0c;这些操作与 Python 的位运算符类似…

网络安全技术简介

网络安全技术简介 随着信息技术的迅猛发展&#xff0c;互联网已经成为人们日常生活和工作中不可或缺的一部分。与此同时&#xff0c;网络安全问题也日益凸显&#xff0c;成为全球关注的焦点。无论是个人隐私泄露、企业数据被盗取还是国家信息安全受到威胁&#xff0c;都与网络…

[Python学习日记-80] 用 socket 实现文件传输功能(上传下载)

[Python学习日记-80] 用 socket 实现文件传输功能&#xff08;上传下载&#xff09; 简介 简单版本 函数版本 面向对象版本 简介 到此为止网络编程基础的介绍已经接近尾声了&#xff0c;而在本篇当中我们会基于上一篇博客代码的基础上来实现文件传输功能。文件传输其实与远…