ZYNQ QSPI flash分区设置&启动配置
ahcoder 2025-05-24 11:38 7 浏览
需求:
一款基于zynq架构的产品,只有qspi flash,并没有其他的存储设备,
现在的要求固化某个应用程序app,设置开机启动.
但是根据厂家提供的sdk,编译出的镜像重启后,文件系统的内容都会还原,
之前的方案是每次都要把程序放到buildroot下,
然后重新编译,将rootfs、内核镜像、设备树打包到image.ub.bin中,
然后用jtag重新烧录到flash中。
这很不合理,所以要我们需要对flash进行分区,
然后将需要固化的程序通过flashcp烧写到flash中,然后在用dd命令导出该文件。
0. MTD基础
该操作依赖linux的MTD子系统。
MTD(Memory Technology Device)是内存技术设备,它为原始闪存设备(例如NAND,OneNAND,NOR 等)提供了一个抽象层。
这些不同类型的Flash都可以使用相同的API。
通常内核都默认支持MTD驱动。
MTD字符设备-通常称为**/dev/mtd0,/dev/mtd1**等。
这些字符设备提供对原始闪存的I/O访问。
它们支持许多ioctl调用,用于擦除擦除块,将其标记为不良或检查擦除块是否不良,获取有关MTD设备的信息等。
sysfs接口,它提供有关系统中每个MTD设备的完整信息。 此接口易于扩展,并且鼓励开发人员尽可能使用sysfs接口,而不是较旧的ioctl或/proc/mtd接口。
mtd子系统的sysfs接口已在内核中进行了说明,当前可在Documentation/ABI/ testing/sysfs-class-mtd中找到。
/proc/mtd proc文件系统文件提供常规的MTD信息。 这是旧版界面,而sysfs界面提供了更多信息。
MTD子系统支持带有软件和硬件ECC的 raw NAND闪存,OneNAND闪存,CFI(通用闪存接口)NOR闪存以及其他类型的闪存。
1. 查看qspi flash大小
进入uboot
fmsh> sf probe 0
SF: Detected n25q256 with page size 256 Bytes, erase size 4 KiB, total 32 MiB
该命令式查看设备信息。
可以看到qspi flash容量为32MB,即0x1E84800
2. 需要固化镜像分区地址设置
一口君使用的平台需要固化2个文件:cfg(存储配置信息)、app(可执行程序)
加上必须烧录的boot.bin、image.ub.bin,一共有4个文件,
所以我们需要配置4个分区。
1) boot.bin、image.ub.bin地址
其中boot.bin包含了fpga的ip核和启动必要的文件信息,地址固定为0
image.ub.bin的地址通常厂家也会给出默认地址,
进入uboot打印环境信息:
fmsh> printenv
fit_size=0x153f000
flash_off=0x500000
load_addr=0x2000000
qspiboot=echo Copying FIT from SPI flash to RAM...
&& sf probe && sf read ${load_addr} ${flash_off} ${fit_size} && bootm ${load_addr}
echo Copying FIT from SPI flash to RAM... :
打印提示信息
sf probe:
查看设备硬件信息
sf read ${load_addr} ${flash_off} ${fit_size},
从flash地址flash_off开始读取fit_size个字节到ram地址load_addr
bootm ${load_addr}:
启动内核
可以看到flash地址是flash_off:0x500000
2) 分区划分
那现在我们就可以给这4个文件设置分区信息了
镜像文件实际大小(hex)起始地址offset块数boot.bin3D09000x000000x50000061-80image.ub.binD59F800x5000000x1100000214-272cfg.bin2000x16000000x100001app.bin78000x16100000x300003
注意:
offset大小必须是 0x10000整数倍,这个是擦除的最小单位-块。
每个分区大小结合要固化的程序,合理分配,既要考虑后面程序升级需要预留足够空间,也不要太大,造成浪费
分区划分不能超过flash最大值32M
3. 设备树
flash分区设备树说明如下:
Documentation\devicetree\bindings\mtd\partition.txt
Fixed Partitions
================
Partitions can be represented by sub-nodes of a flash device. This can be used
on platforms which have strong conventions about which portions of a flash are
used for what purposes, but which don't use an on-flash partition table such
as RedBoot.
The partition table should be a subnode of the flash node and should be named
'partitions'. This node should have the following property:
- compatible : (required) must be "fixed-partitions"
Partitions are then defined in subnodes of the partitions node.
For backwards compatibility partitions as direct subnodes of the flash device are
supported. This use is discouraged.
NOTE: also for backwards compatibility, direct subnodes that have a compatible
string are not considered partitions, as they may be used for other bindings.
#address-cells & #size-cells must both be present in the partitions subnode of the
flash device. There are two valid values for both:
<1>: for partitions that require a single 32-bit cell to represent their
size/address (aka the value is below 4 GiB)
<2>: for partitions that require two 32-bit cells to represent their
size/address (aka the value is 4 GiB or greater).
Required properties:
- reg : The partition's offset and size within the flash
Optional properties:
- label : The label / name for this partition. If omitted, the label is taken
from the node name (excluding the unit address).
- read-only : This parameter, if present, is a hint to Linux that this
partition should only be mounted read-only. This is usually used for flash
partitions containing early-boot firmware images or data which should not be
clobbered.
- lock : Do not unlock the partition at initialization time (not supported on
all devices)
我们只需要关注分区的子节点说明即可:
- reg描述某个flash分区的offset和size
- label(可选)分区名字
- read-only(可选)该分区只读
根据前面所有分析内容,最终我们修改设备信息如下:
&qspi0 {
status = "okay";
flash0: s25fl256s@0 {
compatible = "spi-flash","spansion,s25fl256s1", "jedec,spi-nor";
reg = <0>; /* chip select */
spi-max-frequency = <50000000>;
m25p,fast-read;
page-size = <256>;
block-size = <16>; /* 2^16, 64KB */
cdns,read-delay = <2>;
cdns,tshsl-ns = <0>;
cdns,tsd2d-ns = <0>;
cdns,tchsh-ns = <0>;
cdns,tslch-ns = <0>;
#address-cells = <1>;
#size-cells = <1>;
partition@boot {
label = "boot";
reg = <0x0000000 0x500000>;
};
partition@uimage.ub {
label = "uimage.ub";
reg = <0x500000 0x1100000>;
};
partition@prm {
label = "cfg";
reg = <0x1600000 0x10000>;
};
partition@kk_ap {
label = "app";
reg = <0x1610000 0x30000>;
};
};
};
重新编译rootfs打包后重新启动即可。
4. 查看分区信息
# cat /proc/mtd
dev: size erasesize name
mtd0: 00500000 00010000 "boot"
mtd1: 01100000 00010000 "uimage.ub"
mtd2: 00010000 00010000 "cfg"
mtd3: 00030000 00010000 "app"
# ls /dev/mtd* -l
crw------- 1 root root 90, 0 Jan 1 00:00 /dev/mtd0
crw------- 1 root root 90, 1 Jan 1 00:00 /dev/mtd0ro
crw------- 1 root root 90, 2 Jan 1 00:00 /dev/mtd1
crw------- 1 root root 90, 3 Jan 1 00:00 /dev/mtd1ro
crw------- 1 root root 90, 4 Jan 1 00:00 /dev/mtd2
crw------- 1 root root 90, 5 Jan 1 00:00 /dev/mtd2ro
crw------- 1 root root 90, 6 Jan 1 00:00 /dev/mtd3
crw------- 1 root root 90, 7 Jan 1 00:00 /dev/mtd3ro
brw------- 1 root root 31, 0 Jan 1 00:00 /dev/mtdblock0
brw------- 1 root root 31, 1 Jan 1 00:00 /dev/mtdblock1
brw------- 1 root root 31, 2 Jan 1 00:00 /dev/mtdblock2
brw------- 1 root root 31, 3 Jan 1 00:00 /dev/mtdblock3
/dev/mtd0,/dev/mtd0ro,/dev/mtdblock0代表的是同一个MTD分区,但是**/dev/mtd0,/dev/mtd0ro都是字符设备,其中/dev/mtd0ro是只读字符设备,/dev/mtdblock0是块设备。 常见的mtd-utils,nand_write等工具只能操作/dev/mtdX**字符设备,因为只有字符设备才支持ioctl操作。
5. 拷贝读取 MTD 分区
- 查看 MTD 分区cat /proc/mtd
- 擦除 MTD 分区flash_eraseall /dev/mtdX
擦除/dev/mtd0分区的第1块数据。
flash_erase /dev/mtd0 0x0 1
- 写 MTD 分区 NOR Flashflashcp /tmp/mtd.bin /dev/mtdX
- 写 MTD 分区 NAND Flashnandwrite /tmp/image.bin /dev/mtdX
- 读 MTD 分区dd if=/dev/mtdX of=/tmp/mtd.bin
a) 烧写cfg.bin文件到mtd2
首先需要下载文件导开发板,可以用sd卡、网口(tftp)、串口(rz命令),根据自己的开发板资源。
执行下面命令烧录:
flash_erase /dev/mtd2 0x0 1
flashcp cfg.bin /dev/mtd2
导出分区文件
dd if=/dev/mtd2 of=/mnt/cfg.bin
b) 烧写app.bin到mtd3
flash_erase /dev/mtd3 0x0 3
flashcp app /dev/mtd3
导出分区文件
dd if=/dev/mtd3 of=/mnt/app.bin
6. 还原文件
注意导出的文件除了我们烧录的文件之外,
尾部还有多余FF,所以还需要去掉这些多余的部分,
所以我们必须要还原文件。
如下图所示:
【文件必须以二进制形式打开才能看到,彭老师用的Hex Editor Neo】
下载地址:
https://hhdsoftware.com/free-hex-editor
还原文件有很多方法,一口君自己写了个小程序,
原理:
逐字节读取文件,然后判断是否是0xFF,连续读取到16个0xff(防止文件中也由多个0XFF出现),
则认为读到了有效文件尾部,记录有效文件长度,然后根据该长度,复制成最终文件,该文件就是我们所需要的最终文件。
源码:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char** argv)
{
int fd_p;
int fdw_p;
unsigned char c;
int count = 0;
int pos = 0;
int i;
if(argc != 3)
{
printf("argument error\n");
for(int i = 0; i < argc ; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}
}
fd_p = open(argv[1], O_RDWR);
if(fd_p < 0){
printf("open file %s failed\n", argv[1]);
return -1;
}
fdw_p = open(argv[2], O_RDWR | O_CREAT);
if(fdw_p < 0){
printf("open file %s failed\n", argv[2]);
return -1;
}
while(1){
read(fd_p, &c, 1);
if(c == 0xff){
count++;
if(count >= 16){
break;
}
}
else{
count = 0;
}
pos++;
}
lseek(fd_p, SEEK_SET, 0);
for(i=0; i<pos-15; i++){
read(fd_p, &c, 1);
write(fdw_p, &c, 1);
}
return 0;
}
测试:
可见写入到分区的文件和我们从分区读取后再还原的文件时一致的。
重启后,再验证
从MD5校验码可知,可执行程序还原正确。
7. 开机自动还原文件
要想开机后自动还原该文件,并启动程序app,步骤如下:
- 将exportimg拷贝文件系统**/mnt**下,(目录有执行权限即可)
- 设置开机子启动脚本
sdk/buildroot-xxxxxx/output/target/etc/init.d/rcS
文件尾部添加:
dd if=/dev/mtd2 of=/mnt/cfg.bin
dd if=/dev/mtd3 of=/mnt/app.bin
touch /mnt/app
touch /mnt/cfg
chmod 777 /mnt/app
chmod 777 /mnt/cfg
/mnt/exportimg /mnt/app.bin /mnt/app
/mnt/exportimg /mnt/cfg.bin /mnt/cfg
rm /mnt/cfg.bin
rm /mnt/app.bin
/mnt/app &
本例在复旦微fmsh平台测试通过。 zynq平台应该也没问题。
有了MTD驱动,就可以将挂载jffs2分区。
后续会继续更新文章。
- 上一篇:10 个鲜为人知但非常有用的 Linux 命令
- 下一篇:Linux进程
相关推荐
- 当 Linux 根分区 (/) 已满时如何释放空间?
-
根分区(/)是Linux文件系统的核心,包含操作系统核心文件、配置文件、日志文件、缓存和用户数据等。当根分区满载时,系统可能出现无法写入新文件、应用程序崩溃甚至无法启动的情况。常见原因包括:「日志文件...
- linux系统监控工具小神器:btop(linux网络监控工具)
-
top是大家常用的实时系统监控工具,今天给大家介绍一款非常酷炫的实时系统监控工具btop,用了之后你一定会爱上它!btop是一个高级的实时系统监控工具,它是传统top命令的现代替代品,提供了丰富...
- 又一全新恶意软件曝光!专门针对Windows、Linux 和 macOS 用户
-
近日,网络安全研究人员发现了一个利用“CheanaStealer”恶意软件的复杂网络钓鱼活动,该恶意软件是通过一个VPN钓鱼网站传播的。这次攻击的主要目标是各种操作系统的用户,包括Wind...
- Java程序员必备的Linux命令全解析
-
Java程序员必备的Linux命令全解析作为一名Java开发者,除了精通Java语法和框架外,掌握一些基础的Linux命令也是十分必要的。这不仅能提高你的工作效率,还能让你更好地管理和部署Java应用...
- Linux基础知识之shell实现用户管理功能
-
[root@k8s-mastershell]#moreusermanager.sh#!/bin/bashRED='\033[0;31m'GREEN='\033[...
- 惊艳!Linux 中迷人的 Shell 脚本工具
-
如果您是Linux操作系统爱好者或正在将自己定义为一个爱好者,那么与shell脚本交叉的路径是完全不可避免的。根据定义,shell脚本是设计用于在Unix/Linuxshell环境中执...
- 【shell编程】你的第一个sh脚本(shell脚本编程教程)
-
vimhello.sh#!/bin/bash#注释echo-e"HelloWorld!\a\n"exit0第一行#!/bin/bash,作用是宣告这个文件内的语...
- linux之bash、sh和dash(linux里bash命令)
-
linux系统里有sh、bash、dash等多种shell的解释器命令,其中sh是shll的缩写,是linux系统默认的shell解释器,bash则是sh命令的增强版,dash则是从netbsd派生而...
- 14、linux命令-du(linux命令-s)
-
14、linux命令-du常用命令du-sh/*#显示指定目录下每个文件或目录的容量大小,并且以易读方式显示(常用)。du命令概述du命令作用是估计文件系统的磁盘已使用量,常用于查看文件或目录...
- Manjaro Linux:属于我的Linux体验
-
从Debiantesting切换回Manjarotesting前端使用好久的Manjarotesting切换到了Debiantesting,就是因为有一些包只有deb版本,适配了Debia...
- 小狼毫 0.17.0 更新,解锁输入新姿势!
-
0.17.0版本(2025年5月17日发布)这次更新可不少东西呢!先把librime升级到1.13.1版本啦,也不知道这升级之后会带来啥新变化,用用就知道咯。之前老是出问题的托...
- Kali Linux 初始配置(kali linux2019默认用户名和密码)
-
1.更新源&升级系统sudoaptupdate&&sudoaptupgrade-ysudoaptdist-upgrade-y作用:确保所有工具和系统补丁为最新,避免...
- 怎样利用锤子手机和讯飞手机输入法,让电脑动起来
-
在大家看来,老罗的发布会捧红了科大讯飞。小编当时就被老罗洗脑了,立刻下载了讯飞输入法体验了一番。后来小编突发奇想,我经常使用向日葵远程控制电脑,如果远程控制电脑时使用讯飞,能否在电脑上完成语音输入?或...
- 装好KALI之后,急需做的两个事情,更新源和添加输入法
-
每次当我们装完系统之后,突然发现很茫然,为什么要装这个系统?也就是说我们压根儿就不知道装这个系统是为了做什么。而且刚装好的系统体验起来,好像也并没有网上说的那么好。之前想做的种种操作现在也就不了了之了...
- Linux 依赖问题“硬核”解决方案 | 技术
-
编者按:本文介绍了一些另类的暴力破解RPM和DEB软件包依赖关系的方法,对陷入依赖陷阱而不可自拔的人来说,有时候这也是一种绝地求生之路。至于说这样做是否合适,那就是一件见仁见智的事情了,不过这...
- 一周热门
- 最近发表
- 标签列表
-
- linux 远程 (37)
- u盘 linux (32)
- linux 登录 (34)
- linux 路径 (33)
- linux 文件命令 (35)
- linux 是什么 (35)
- linux 界面 (34)
- 查看文件 linux (35)
- linux 语言 (33)
- linux代码 (32)
- linux 查看命令 (33)
- 关闭linux (34)
- root linux (33)
- 删除文件 linux (35)
- linux 主机 (34)
- linux与 (33)
- linux 函数 (35)
- linux .ssh (35)
- cpu linux (35)
- 查看linux 系统 (32)
- linux 防火墙 (33)
- linux 手机 (32)
- linux 镜像 (34)
- linux ip地址 (34)
- linux 用户查看 (33)