Qemu开发ARM篇-6、emmc/SD卡AB分区镜像制作并通过uboot进行挂载启动
文章目录
- 1、AB分区镜像制作
- 2、uboot修改
- 3、镜像启动
在上一篇 Qemu开发ARM篇-5、buildroot制作根文件系统并挂载启动中,我们通过buildroot制作了根文件系统,并通过
SD卡的形式将其挂载到设备并成功进行了启动,但上一章中,我们的做法非常简单粗暴,并且没有进行分区,在本章中我们将对对
SD镜像进行分区,并制作
AB分区的镜像。
1、AB分区镜像制作
uboot环境变量镜像制作:
1、在工程目录新建etc文件,并在里面新建uboot_env.txt文件,该文件用来描述所使用的环境变量,后续我们将会将其制作为uboot_env的环境变量镜像,并将其写入emmc(SD)分区中,uboot将从这里面去读取环境变量。
mkdir etc && cd etc && touch uboot_env.txt

并在uboot_env.txt中输入如下内容:
bootcmd=mmc read 0x60003000 0x3800 0x10000;mmc read 0x60500000 0x1800 0x400;bootm 0x60003000 - 0x60500000
bootargs=root=/dev/mmcblk0p9 init=/usr/sbin/init console=ttyAMA0
上面参数以及地址什么意思?别急,我们先将整个镜像制作出来在来说明上述参数含义,因为上面的参数都是根据我们制作的镜像进行计算的.
2、在script目录使用touch make_AB_img.sh && chmod 777 make_AB_img.sh命令创建AB分区镜像制作脚本,并在制作脚本输入如下内容:
#!/bin/bash
ROOT_PATH=$(pwd)
OUT_PATH=$ROOT_PATH/out/ab_img
if [ -e $OUT_PATH ]
thenrm -rf $OUT_PATH
fi
mkdir $OUT_PATH -p
cd $OUT_PATH
# 磁盘总大小为8G
dd if=/dev/zero of=vexpress_ab.img bs=1M count=8192
# msic part 512K
sgdisk -n 0:0:+512k -c 0:misc vexpress_ab.img
# ubootenv part 512K 环境变量存放位置
sgdisk -n 0:0:+512k -c 0:ubootenv vexpress_ab.img
# dtb_a 512k 设备树
sgdisk -n 0:0:+512k -c 0:dtb_a vexpress_ab.img
# dtb_b 512k 设备树
sgdisk -n 0:0:+512k -c 0:dtb_b vexpress_ab.img
# vbmeta_a part 512K 安全校验
sgdisk -n 0:0:+512k -c 0:vbmeta_a vexpress_ab.img
# vbmeta_b part 512K
sgdisk -n 0:0:+512k -c 0:vbmeta_b vexpress_ab.img
# boot_a partition 32M kernel分区
sgdisk -n 0:0:+32M -c 0:boot_a vexpress_ab.img
# boot_b partition 32M kernel分区
sgdisk -n 0:0:+32M -c 0:boot_b vexpress_ab.img
# rootfs_a partition 512M
sgdisk -n 0:0:+512M -c 0:rootfs_a vexpress_ab.img
# rootfs_b partition 512M
sgdisk -n 0:0:+512M -c 0:rootfs_b vexpress_ab.img
# userdata partition 剩余所有大小分配给userdata分区
sgdisk -n 0:0:0 -c 0:userdata vexpress_ab.imgsync
# 显示分区信息
sgdisk -p vexpress_ab.img# 读取分区信息并挂载到回环设备
LOOPDEV=`losetup -f`
echo $LOOPDEV
sudo losetup $LOOPDEV vexpress_ab.img
sudo partprobe $LOOPDEV
sudo losetup -l
ls -l $LOOPDEV*# 将userdata格式化为ext4格式
# sudo mkfs.ext4 ${LOOPDEV}p9
# sudo mkfs.ext4 ${LOOPDEV}p10
sudo mkfs.ext4 ${LOOPDEV}p11# 拷贝rootfs、kernel
mkdir tem_file
cp $ROOT_PATH/out/kernel-arm/arch/arm/boot/uImage tem_file/kernel
cp $ROOT_PATH/out/kernel-arm/arch/arm/boot/dts/vexpress-v2p-ca9.dtb tem_file/vexpress-v2p-ca9.dtb
cp $ROOT_PATH/out/rootfs-arm/images/rootfs.ext4 tem_file/rootfs.ext4
# 制作环境变量
mkenvimage -s 524288 -o tem_file/env.img $ROOT_PATH/etc/uboot_env.txt# 将对应分区数据写入对应分区
sudo dd if=tem_file/env.img of=${LOOPDEV}p2 conv=notrunc
sudo dd if=tem_file/kernel of=${LOOPDEV}p7 conv=notrunc
sudo dd if=tem_file/vexpress-v2p-ca9.dtb of=${LOOPDEV}p3 conv=notrunc
sudo dd if=tem_file/rootfs.ext4 of=${LOOPDEV}p9 conv=notrunc# rootfs是ext4格式,为了能让磁盘空间占满,这里我们进行resize
sudo resize2fs -f ${LOOPDEV}p9# 可以使用下面挂载copy的方式进行rootfs的创建,这里我们采用上面的dd方式
# # 创建临时挂载目录,用来像分区中copy数据
# mkdir tem_rootfs_a
# mkdir tem_userdata
# # 挂载rootfs_a和userdata分区
# sudo mount -t ext4 ${LOOPDEV}p7 ./tem_rootfs_a -o loop
# sudo mount -t ext4 ${LOOPDEV}p9 ./tem_userdata -o loop
# sudo cp -rf rootfs-arm/* ./tem_rootfs_a
# sudo umount ./tem_rootfs_a
# sudo umount ./tem_userdata# 取消回环设备
sudo losetup -d $LOOPDEV
# rmdir ./tem_rootfs_a ./tem_userdata
上面其他命令对应都在代码中进行了相应的注释,这里不在过多的阐述,我们来解释一下上面 mkenvimage -s 524288 -o tem_file/env.img $ROOT_PATH/etc/uboot_env.txt这条制作uboot镜像的命令的含义:
其中-s用来指定制作的uboot环境变量分区的大小(env.img),因为我们在进行分区划分的时候划分的为512k(sgdisk -n 0:0:+512k -c 0:ubootenv vexpress_ab.img),512k=512*1024=524288字节,因此在这里我们制作的uboot环境变量的大小也为512k。
注意:上面镜像中未包含uboot,这是因为我们qemu需要重uboot启动,需要手动指定uboot镜像,因此在AB分区中,我们不制作uboot镜像,另外vbmeta用作安全的,目前我们还未使用到,因此这里暂做保留。
然后运行./script/make_AB_img.sh 制作AB分区镜像。

运行完成之后,会在out/ab_img/目录下生vexpress_ab.img镜像文件。

使用sgdisk查看镜像文件:

可以看到,和我们制作时候指定镜像大小是一致的。
3、uboot_env.txt中环境变量的含义
现在我们已经将镜像制作出来了,现在我们就可以来深究一下上面uboot_env.txt中环境变量的含义了,为什么这么设置,以及为什么要设置为这些值。
首先是boot_cmd:我们设置的值为:bootcmd=mmc read 0x60003000 0x3800 0x10000;mmc read 0x60500000 0x1800 0x400;bootm 0x60003000 - 0x60500000
在uboot运行时候,会自动运行bootcmd的值命令进行内核启动,我们在bootcmd中也就是设置启动参数。
mmc read 0x60003000 0x3800 0x10000解释
这条命令的意思是从emmc的0x3800块地址处读取0x10000块数据到0x60003000内存中。这里其实就是读取kernel的镜像,为什么呢?
使用sgdisk -p命令可以看到制作分区情况,也就是在上面图中,我们可以看到,boot_a的起始块地址为14336,注意这是块地址,每块是512字节,而我们mmc read命令中需要的就是块地址,因此我们这里在使用mmc read读取地址就是14336,将其转换为16进制就是0x3800,
从上图分区情况我们还可以知道,boot_a分区大小为:32M=321024k=3210241024=33554432字节=321024*1024/512=65,536块,转换为16进制为:0x1 0000块,所以上面最后一个参数为0x1 0000。
至于为什么将kernel内容读到内存0x60003000这个地址出,这是因为我们在编译kernel的时候指定了其链接地址为0x60003000,忘记了的可以参考之前的文章:Qemu开发ARM篇-4、kernel交叉编译运行演示
mmc read 0x60500000 0x1800 0x400解释
这条命令意思和上面kernel差不多,这里就快速过一下:
dtb_a起始块地址:6144=0x1800
dtb_a分区大小:512k=5121024字节=5121024/512块=1024块=0x400块
0x60500000 :这里不将kernel覆盖掉的地址理论都可以,这里设置为:0x60500000
bootm 0x60003000 - 0x60500000解释
这条命令是启动kernel的标准命令,前面是内核的地址,后面的dtb的地址,都是上面我们进行指定的。
然后是boot_args:bootargs=root=/dev/mmcblk0p9 init=/usr/sbin/init console=ttyAMA0
bootargs的参数会传递给内核,用于挂载根文件系统以及指定系统初始化程序,这里根文件系统位于
/dev/mmcblk0p9分区,初始化程序选择/usr/sbin/init,这个其实就是systemd,我们在制作根文件系统选择的,如果忘记的可以参考之前的文章:Qemu开发ARM篇-5、buildroot制作根文件系统并挂载启动
2、uboot修改
因为我们将环境变量设置在了分区2,因此我们需要对uboot进行配置,告诉uboot在哪里去获取环境变量:
运行我们之前编译uboot的脚本:./script/build_uboot.sh进入到图形配置界面:
1、找到环境变量配置选项

选项配置如下:

取消掉in flash选项,选中in mmc选项,然后修改环境变量偏移为:0x200000,这是因为我们环境变量起始块地址为:4096块=4096512=2,097,152字节=0x200000,因此偏移为:0x200000
环境变量size设置为0x80000这是因为我们环境变量大小为512k=5121024=524,288字节=0x80000,因此大小为:0x80000
下面分区设备以及分区号都填0,因为我们上面偏移都是从0开始计算的。
设置好之后,保持配置,为了不用每次都进行配置,我们将该配置进行保存,并替换原配置文件,这样我们就不用每次都进行配置了,使用如下命令保存该配置:
cp ./src/uboot/u-boot-2022.07-rc3/configs/vexpress_ca9x4_defconfig ./src/uboot/u-boot-2022.07-rc3/configs/vexpress_ca9x4_defconfig_bak
cp ./out/u-boot-arm/.config ./src/uboot/u-boot-2022.07-rc3/configs/vexpress_ca9x4_defconfig
这样我们下次再编译的时候就会使用我们刚刚配置的配置了。
3、镜像启动
1、uboot信息确认
编译完新的uboot以及制作完分区镜像之后,我们在script目录使用touch run_uboot_with_img.sh && chmod 777 run_uboot_with_img.sh命令创建启动脚本,并在启动脚本输入如下内容:
#!/bin/bash
qemu-system-arm -M vexpress-a9 -m 512M -kernel ~/project/qemu/out/u-boot-arm/u-boot -nographic -no-reboot -sd out/ab_img/vexpress_ab.img
然后运行./script/run_uboot_with_img.sh启动uboot: 启动是按下回车先看看uboot内部情况。

可以看到环境变量加载成功,使用printenv查看一下环境变量是不是我们刚才设置的:

了可以看到环境变量和我们上面设置的一致,在看看其他内容。
在uboot中使用mmcinfo查看磁盘情况:

可以看到容量为8G和我们制作大小一致,使用mmc part查看分区情况:

可以看到分区信息也和我们指定的一致:
使用ls mmc 0:9查看rootfs_a内容情况:

可以看到里面内容正是我们制作的根文件系统。
2、启动
当上面一切都正常后,使用run bootcmd启动内核,看能否正常进入系统:

可以看到已经成功启动了内核并成功挂载了根文件系统并进入了系统。

下一次uboot启动时,不要按回车,uboot就会自动运行bootcmd命令,并启动内核进入系统。

相关文章:
Qemu开发ARM篇-6、emmc/SD卡AB分区镜像制作并通过uboot进行挂载启动
文章目录 1、AB分区镜像制作2、uboot修改3、镜像启动 在上一篇 Qemu开发ARM篇-5、buildroot制作根文件系统并挂载启动中,我们通过buildroot制作了根文件系统,并通过 SD卡的形式将其挂载到设备并成功进行了启动,但上一章中,我们的…...
Spring Boot中使用ThreadPoolTaskScheduler实现轻量级多线程定时任务
引言 在Java开发中,Spring Boot提供了多种方式来执行定时任务,如Scheduled注解和TaskScheduler。当需要执行多线程定时任务时,ThreadPoolTaskScheduler是一个轻量级的解决方案。本文将通过一个具体的业务场景,介绍如何使用Thread…...
完全二叉树的节点个数 C++ 简单问题
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。 示例 1ÿ…...
每日一题学习笔记
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以,返回 true ;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1: 输入&#…...
从事人工智能学习Python还是学习C++?
人工智能(Artificial Intelligence,简称AI)是当今科技领域最热门的研究方向之一。AI 涉及多个学科和技术,特别是机器学习、神经网络、深度学习等技术的应用。在AI的开发过程中,编程语言的选择对于开发效率和项目实现至…...
博客摘录「 CNN中的感受野和有效感受野会对模型产生怎样的影响?」2024年9月29日
,中心像素受影响较大,离中心越远梯度信号越弱。梯度信号的衰减是指数级的,这意味着应用于感受野的大多数像素的梯度将是可忽略的(如果有的话)。 有效感受野的定义...
AURIX单片机示例:开发入门与点亮LED
文章目录 目的模板工程Blinky_LED示例链接总结 目的 这个例程比较简单,主要通过这个例程来介绍 AURIX™ Development Studio(ADS) 和 iLLD 库来开发 AURIX 系列单片机一些入门的内容。一些更为基础的资料等内容可以参考下面文章: 《英飞凌 AURIX TriCo…...
MySQL字符串函数与操作
在编程领域中,字符串操作是数据处理中至关重要的一部分。无论是文本分析、日志处理,还是格式化输出,字符串的操作技能都能极大提高工作效率。在 Python 中,字符串相关的函数和方法为开发者提供了强大的工具,帮助完成各种任务。了解如何灵活运用这些工具,能够有效提升编程…...
HTML+CSS 水滴登录页
文章目录 一、效果演示二、Code1.HTML2.CSS 三、实现思路拆分 一、效果演示 实现了一个水滴登录页的效果。页面包含一个水滴形状的登录框和两个按钮,登录框包括用户名、密码和登录按钮,按钮分别为忘记密码和注册。整个页面的设计非常有创意,采…...
基于Next.js和TailwindCss的TailwindCss
最近在研究 Next.js 和 TailwindCss ,这两天没事的时候就搞了一个 c。 目前工具部署在 Vercel ,欢迎各位体验(能提出意见更好嘿嘿) 体验地址: https://icon.999872.xyz/ 图片预览 👇...
若依开源系统多数据源整合clickhouse数据库详细步骤
1.添加依赖【pom.xml文件】 <!-- clickhouse数据源依赖--><dependency><groupId>ru.yandex.clickhouse</groupId>...
Subdominator:一款针对漏洞奖励计划的子域名安全枚举工具
关于Subdominator Subdominator是一款针对漏洞奖励计划的子域名安全枚举工具,可用于在漏洞搜寻和侦察过程中进行被动子域名枚举。它旨在通过高效枚举子域名和各种免费被动资源来帮助研究人员和网络安全专业人员发现潜在的安全漏洞。 Subdominator 与各种免费和付费…...
[leetcode]516_最长回文子序列
给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。示例 1: 输入:s "bbbab" 输出&a…...
电子相册|智能化电子相册|基于java的电子相册管理系统设计与实现(源码+数据库+文档)
电子相册管理系统 目录 基于java的电子相册管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师&…...
linux项目_c语言:Makefile编写、动态库生成、添加动态库路径
一直想搞懂Linux中Makefile是怎么管理项目的,知识积累到一定程度后,我就做了一个自己的缩小项目去把剩下的细节搞清楚 代码: Service.c: #include <stdio.h> #include "lib_sevr.h" int main(){printf("输入a, b的值…...
Python学习(1):字典、DataFrame的创建方法
1. 字典的创建方法 1.1 直接创建 # 创建一个包含姓名和年龄的字典 person {"name": "Alice", "age": 25}print(person) # 输出:{name: Alice, age: 25} 1.2 使用 dict() 函数 # 使用键值对列表创建字典 person dict(name"…...
async await 介绍 从0手动实现async await
1 async await介绍 async 和 await 是用于处理异步编程的语法糖,它们简化了异步操作的编写,使其看起来像同步代码。通过 async 标记一个函数为异步函数,返回的是一个 Promise 对象,而 await 用来暂停执行,直到 Promise…...
UDP校验和计算及网络中的校验和机制
UDP (User Datagram Protocol) 是一种无连接的传输层协议,它不像 TCP 那样提供可靠的传输保证。虽然 UDP 不保证数据可靠性,但它仍然提供了一个可选的校验和机制来检测数据在传输过程中出现的错误。 理解UDP校验和的计算过程和其在网络中的作用至关重要。…...
如何使用C语言接入Doris数据库
如何使用C语言接入Doris数据库 一、环境准备1. 安装MySQL C API2. Doris数据库环境二、编写C语言接入代码1. 包含必要的头文件2. 编写连接和查询函数3. 编译和运行程序三、注意事项1. 安全性2. 错误处理3. 性能优化4. 兼容性5. 调试和日志记录四、结论Doris(之前称为Palo或Apa…...
DarkLabel 2.4 目标追标注工具介绍
DarkLabel介绍 https://github.com/darkpgmr/DarkLabel 官方地址 视频/图像标注工具,很适合用于目标追踪任务 DarkLabel可以在视频和图像中标注物体的边界框,并附上 ID 和name。还可以用于裁剪视频、从视频中采样训练图像以及对图像区域进行马赛克处理…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
