需求:
一款基于zynq架構(gòu)的產(chǎn)品,只有qspi flash,并沒有其他的存儲設(shè)備,
現(xiàn)在的要求固化某個應(yīng)用程序app,設(shè)置開機(jī)啟動.
但是根據(jù)廠家提供的sdk,編譯出的鏡像重啟后,文件系統(tǒng)的內(nèi)容都會還原,
之前的方案是每次都要把程序放到buildroot下,
然后重新編譯,將rootfs、內(nèi)核鏡像、設(shè)備樹打包到image.ub.bin中,
然后用jtag重新燒錄到flash中。
這很不合理,所以要我們需要對flash進(jìn)行分區(qū),
然后將需要固化的程序通過flashcp燒寫到flash中,然后在用dd命令導(dǎo)出該文件。
0. MTD基礎(chǔ)
該操作依賴linux的MTD子系統(tǒng)。
MTD(Memory Technology Device)是內(nèi)存技術(shù)設(shè)備,它為原始閃存設(shè)備(例如NAND,OneNAND,NOR 等)提供了一個抽象層。
這些不同類型的Flash都可以使用相同的API。
通常內(nèi)核都默認(rèn)支持MTD驅(qū)動。MTD字符設(shè)備-通常稱為**/dev/mtd0,/dev/mtd1**等。
這些字符設(shè)備提供對原始閃存的I/O訪問。
它們支持許多ioctl調(diào)用,用于擦除擦除塊,將其標(biāo)記為不良或檢查擦除塊是否不良,獲取有關(guān)MTD設(shè)備的信息等。
sysfs接口,它提供有關(guān)系統(tǒng)中每個MTD設(shè)備的完整信息。 此接口易于擴(kuò)展,并且鼓勵開發(fā)人員盡可能使用sysfs接口,而不是較舊的ioctl或/proc/mtd接口。
mtd子系統(tǒng)的sysfs接口已在內(nèi)核中進(jìn)行了說明,當(dāng)前可在Documentation/ABI/ testing/sysfs-class-mtd中找到。
/proc/mtd proc文件系統(tǒng)文件提供常規(guī)的MTD信息。 這是舊版界面,而sysfs界面提供了更多信息。
MTD子系統(tǒng)支持帶有軟件和硬件ECC的 raw NAND閃存,OneNAND閃存,CFI(通用閃存接口)NOR閃存以及其他類型的閃存。
1. 查看qspi flash大小
進(jìn)入uboot
fmsh>?sf?probe?0
SF:?Detected?n25q256?with?page?size?256?Bytes,?erase?size?4?KiB,?total?32?MiB
該命令式查看設(shè)備信息。
可以看到qspi flash容量為32MB,即0x1E84800
2. 需要固化鏡像分區(qū)地址設(shè)置
一口君使用的平臺需要固化2個文件:cfg(存儲配置信息)、app(可執(zhí)行程序)
加上必須燒錄的boot.bin、image.ub.bin,一共有4個文件,
所以我們需要配置4個分區(qū)。
1) boot.bin、image.ub.bin地址
其中boot.bin包含了fpga的ip核和啟動必要的文件信息,地址固定為0
image.ub.bin的地址通常廠家也會給出默認(rèn)地址,
進(jìn)入uboot打印環(huán)境信息:
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:
?查看設(shè)備硬件信息
sf?read?${load_addr}?${flash_off}?${fit_size},
?從flash地址flash_off開始讀取fit_size個字節(jié)到ram地址load_addr
bootm?${load_addr}:
?啟動內(nèi)核
可以看到flash地址是flash_off:0x500000
2) 分區(qū)劃分
那現(xiàn)在我們就可以給這4個文件設(shè)置分區(qū)信息了
鏡像 | 文件實際大小(hex) | 起始地址 | offset | 塊數(shù) |
---|---|---|---|---|
boot.bin | 3D0900 | 0x00000 | 0x500000 | 61-80 |
image.ub.bin | D59F80 | 0x500000 | 0x1100000 | 214-272 |
cfg.bin | 200 | 0x1600000 | 0x10000 | 1 |
app.bin | 7800 | 0x1610000 | 0x30000 | 3 |
注意:
offset大小必須是 ?0x10000整數(shù)倍,這個是擦除的最小單位-塊。
每個分區(qū)大小結(jié)合要固化的程序,合理分配,既要考慮后面程序升級需要預(yù)留足夠空間,也不要太大,造成浪費(fèi)
分區(qū)劃分不能超過flash最大值32M
3. 設(shè)備樹
flash分區(qū)設(shè)備樹說明如下:
Documentationdevicetreebindingsmtdpartition.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)
我們只需要關(guān)注分區(qū)的子節(jié)點說明即可:
reg
描述某個flash分區(qū)的offset和size
label(可選)
分區(qū)名字
read-only(可選)
該分區(qū)只讀
根據(jù)前面所有分析內(nèi)容,最終我們修改設(shè)備信息如下:
&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. 查看分區(qū)信息
#?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分區(qū),但是**/dev/mtd0,/dev/mtd0ro都是字符設(shè)備,其中/dev/mtd0ro是只讀字符設(shè)備,/dev/mtdblock0是塊設(shè)備。
常見的mtd-utils,nand_write等工具只能操作/dev/mtdX**字符設(shè)備,因為只有字符設(shè)備才支持ioctl操作。
5. 拷貝讀取 MTD 分區(qū)
查看 MTD 分區(qū)
cat?/proc/mtd
擦除 MTD 分區(qū)
flash_eraseall?/dev/mtdX
擦除/dev/mtd0分區(qū)的第1塊數(shù)據(jù)。
flash_erase?/dev/mtd0?0x0?1
寫 MTD 分區(qū) NOR Flash
flashcp?/tmp/mtd.bin?/dev/mtdX
寫 MTD 分區(qū) NAND Flash
nandwrite?/tmp/image.bin?/dev/mtdX
讀 MTD 分區(qū)
dd?if=/dev/mtdX?of=/tmp/mtd.bin
a) 燒寫cfg.bin文件到mtd2
首先需要下載文件導(dǎo)開發(fā)板,可以用sd卡、網(wǎng)口(tftp)、串口(rz命令),根據(jù)自己的開發(fā)板資源。
執(zhí)行下面命令燒錄:
flash_erase?/dev/mtd2?0x0?1
flashcp?cfg.bin?/dev/mtd2
導(dǎo)出分區(qū)文件
dd?if=/dev/mtd2?of=/mnt/cfg.bin
b) 燒寫app.bin到mtd3
flash_erase?/dev/mtd3?0x0?3
flashcp?app?/dev/mtd3
導(dǎo)出分區(qū)文件
dd?if=/dev/mtd3?of=/mnt/app.bin
6. 還原文件
注意導(dǎo)出的文件除了我們燒錄的文件之外,
尾部還有多余FF,所以還需要去掉這些多余的部分,
所以我們必須要還原文件。
如下圖所示:
【文件必須以二進(jìn)制形式打開才能看到,彭老師用的Hex Editor Neo】
下載地址:
https://hhdsoftware.com/free-hex-editor
還原文件有很多方法,一口君自己寫了個小程序,
原理:
逐字節(jié)讀取文件,然后判斷是否是0xFF,連續(xù)讀取到16個0xff(防止文件中也由多個0XFF出現(xiàn)),
則認(rèn)為讀到了有效文件尾部,記錄有效文件長度,然后根據(jù)該長度,復(fù)制成最終文件,該文件就是我們所需要的最終文件。
源碼:
#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?errorn");
??for(int?i?=?0;?i?<?argc?;?i++)
??{
???printf("argv[%d]?=?%sn",?i,?argv[i]);
??}
?}
?fd_p?=?open(argv[1],?O_RDWR);
?if(fd_p?<?0){
??printf("open?file??%s?failedn",?argv[1]);
??return?-1;
?}
?fdw_p?=?open(argv[2],?O_RDWR?|?O_CREAT);
?if(fdw_p?<?0){
??printf("open?file?%s?failedn",?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;
}
測試:
可見寫入到分區(qū)的文件和我們從分區(qū)讀取后再還原的文件時一致的。
重啟后,再驗證
從MD5校驗碼可知,可執(zhí)行程序還原正確。
7. 開機(jī)自動還原文件
要想開機(jī)后自動還原該文件,并啟動程序app,步驟如下:
- 將exportimg拷貝文件系統(tǒng)**/mnt**下,(目錄有執(zhí)行權(quán)限即可)設(shè)置開機(jī)子啟動腳本
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?&?
本例在復(fù)旦微fmsh平臺測試通過。
zynq平臺應(yīng)該也沒問題。