0x1. 簡單說幾句
前幾天某寶買了一個海思3516DV300小板子募闲,打算玩玩它的NNIE肴熏,這玩意1T的算力還算不錯了忽孽,值得玩下甥材。但是買來之后發(fā)現(xiàn)被坑了盯另,店家給刷寫的程序不帶那個telnetd,只能使用串口連接上去擂达,但是那個板子帶一個店家給的攝像頭應用土铺,它開機自啟還開了看門狗胶滋。串口登錄上去,會不停打印日志悲敷,停了之后又自動重啟究恤。這就非常難受。和店家問了下后德,只能自己從頭開始搞它的固件了部宿,作為一個算法工程師(劃掉,實際是搬磚工)瓢湃,我用我為數(shù)不多的嵌入式知識踩了不少坑理张,所以打算寫點啥記下來。
店家給了Uboot绵患、內(nèi)核的uImage雾叭、rootfs,由于這個板子硬件是店家開發(fā)的落蝙,需要對uboot和內(nèi)核做修改织狐,所以,這里直接用他們提供內(nèi)核還有uboot筏勒,然后偷梁換柱移迫,換個自己編譯的rootfs上去。
0x2. 準備
1.海思SDK
首先管行,得有海思的SDK厨埋,這里需要海思的編譯器
arm-himix200-linux-*
,并在終端里已經(jīng)配置了環(huán)境變量捐顷。編譯海思rootfs需要的文件荡陷,在 Hi3516CV500R001C02SPC020.rar這個壓縮包里
解壓了這個壓縮包后,執(zhí)行里面的sdk.unpack腳本套菜。海思3516DV300和3516CV500有很多東西是相同的亲善,所以在這個SDK里面,可以看到很多CV500的東西逗柴,但是實際上也給DV300用蛹头,這并不奇怪。
2. 安裝依賴戏溺、配置環(huán)境
安裝一些依賴渣蜗,我這里使用的是Ubuntu 18.04的環(huán)境。需要安裝如下的包旷祸,這些直接用apt-get install
來安裝就可以了耕拷。
zlib1g-dev liblzo2-dev uuid-dev pkg-config libuuid1 bison
mtd-utils u-boot-tools libncurses5-dev libcrypto++-dev
binutils uuid uuid-dev gpref make gcc g++
海思的編譯,有一個坑托享,是文檔里需要安裝的依賴庫和實際上需要的并不相同
我很不明白骚烧,為啥浸赫,編譯一個rootfs,還要裝TeX的工具赃绊?難道是編譯的時候生成文檔既峡?還是要編譯的時候去寫論文?我沒裝這些也編譯過了氨滩椤运敢?還有一個,就是編譯gdb等海思芯片工具的時候忠售,有些依賴庫需要安裝传惠,但是并沒有在文檔里寫,比如bison和gpref稻扬。
3. 基礎的嵌入式Linux知識
這里需要對rootfs有一點點了解卦方。
如上圖(摘自海思文檔Hi3516CV500╱Hi3516DV300╱Hi3516AV300 開發(fā)環(huán)境用戶指南),rootfs包含的內(nèi)容就是這樣的泰佳。嵌入式Linux系統(tǒng)啟動的時候愿汰,會從這個文件系統(tǒng)中,調(diào)用init這個程序乐纸,init作為Linux系統(tǒng)啟動的第一個進程,會從這里fork出很多其他的進程來摇予。Linux系統(tǒng)的啟動過程可以分為5個階段:內(nèi)核的引導汽绢、運行 init、系統(tǒng)初始化侧戴、建立終端宁昭、用戶登錄系統(tǒng)。在嵌入式環(huán)境下酗宋,由uboot來加載內(nèi)核积仗,然后內(nèi)核加載完成后,運行init蜕猫,這樣就完成了系統(tǒng)的啟動寂曹。
在這里,我們是用busybox來完成rootfs的編譯的回右。busybox是一個集成了一百多個最常用linux命令和工具的軟件,它集成了init隆圆、shell,甚至還集成了一個http服務器和一個telnet服務器翔烁,而所有這一切功能卻只占了很小的空間渺氧。
0x3. rootfs編譯與打包
1. Makefile的全部編譯目標
這里,我們看海思提供的Makefile蹬屹,在osdrv下的這一個侣背。我們可以看下白华,這個Makefile可以編譯什么東西。在剛才的make all 的過程中贩耐,可以看到弧腥,整個系統(tǒng)的編譯是分為幾個部分的。這里憔杨,我們分別來看每個部分鸟赫。
其中,紅圈是我們需要的消别,看名字就可以知道它是干啥的了抛蚤,通過hiboardtool、hipctool是編譯我們需要板子寻狂、電腦上的工具岁经,包括在板子上調(diào)試代碼的gdb、電腦上打包rootfs用的mkyaffs2image100等蛇券;hibusybox是編譯busybox的缀壤,hirootfs是打包編譯完成的busybox到可用的rootfs鏡像的。
2. 按照官方說明編譯全部內(nèi)容(可選)
這一步的目的是為了了解海思編譯的過程纠亚,然后塘慕,后面編譯工作可以更順利,當然也可以手動執(zhí)行這些操作。此外,通過make all可以檢查依賴庫是否安裝正確幅慌,然后編譯出我們需要的工具來,這樣后面就不用再考慮工具的事情了蛤织。
網(wǎng)上有些博主說,直接在海思osdrv那個文件夾下執(zhí)行make all鸿染,會有坑指蚜,容易編譯不過。其實涨椒,這都是海思文檔的鍋摊鸡,那個文檔并不全面。在osdrv下的那個Makefile文件丢烘,里面內(nèi)容非常多柱宦,不過有大坑,如果可以讀懂那個Makefile播瞳,并做出合適的修改掸刊,后面構建rootfs等操作就會非常順利。
這里赢乓,按照那個readme_cn.txt忧侧,下載了那些開源代碼的源碼文件石窑,放到相應的位置,就可以開始編譯了蚓炬。直接執(zhí)行make all就可以了松逊,這樣默認編譯出來的是3516DV平臺的文件。這里首先跑一遍make all肯夏,來編譯全部的東西经宏,包括電腦上用的那些工具,后面就可以只編譯一部分了驯击。我的電腦是AMD R5 2600X的CPU烁兰,編譯一次大約10分鐘。如果報錯徊都,那就是的問題沪斟。
完成編譯后,可以看到已經(jīng)按照默認配置生成了一些rootfs文件暇矫、uImage還有uboot文件主之。這個uImage和uboot是沒修改過的,不一定可以用李根。這里只看它生成的rootfs文件槽奕。
此外,在osdrv/pub/bin下房轿,會編譯出板子和PC上用的工具史翘,包括鏡像制作工具、GDB調(diào)試工具等冀续,在hi3516dv300_spi_smp_image_glibc下,是編譯出的uboot必峰、uImage和rootfs洪唐。
3. 修改rootfs然后編譯
如果用make all編譯出的rootfs鏡像,是不方便的吼蚁,因為這里沒把NNIE之類的驅動打包進去凭需,同時,并沒有為板子上設置/tmp分區(qū)肝匆,而且粒蜈,編譯的busybox里面有相當多的冗余的東西。所以這里需要對編譯的過程進行修改旗国,然后手動編譯并將驅動放進去枯怖。
我們的目標是build出帶有驅動、相關配置的可以直接用的rootfs來能曾。這里我們先看busybox的部分度硝。
看紅圈的部分肿轨,有沒有感覺不太對?這是海思的另一個坑蕊程,編譯完了椒袍,做出修改之后重新編譯,相當于沒修改藻茂。所以驹暑,我們手動編譯這些東西。此外辨赐,使用這個編譯出來的rootfs优俘,busybox是有不少多余的東西的,通過手動配置肖油,可以更加精簡兼吓。這里,我們把紅圈的內(nèi)容都注釋掉森枪,然后在
osdrv/opensource/busybox/busybox-1.26.2
下视搏,直接執(zhí)行cp config_v200_a7_softfp_neon .config && make menuconfig
,然后進入配置界面县袱。這里浑娜,我把Print Utilities下的所有內(nèi)容關了,把shells下的hush關了式散,這些可以根據(jù)自己的想法來配置筋遭。配置完成后保存并退出,再執(zhí)行
make -j && make install
來編譯busybox暴拄,這時候漓滔,編譯的結果在_install文件夾下,我們已經(jīng)完成了busybox的編譯乖篷。
4. 添加驅動响驴、配置啟動設置
系統(tǒng)在啟動的時候,需要加載驅動撕蔼,然后配置網(wǎng)絡豁鲤,所以需要修改配置項,然后讓系統(tǒng)可以自動完成驅動加載等任務鲸沮。
這里琳骡,我只需要開發(fā)NNIE的應用,可能用到TDE讼溺、IVE等模塊楣号。驅動在這里
海思提供了一個rootfs的框架(也可以看做是一個類似于模板的東西吧),我們可以在這個的基礎上進行配置,把編譯的busybox復制進去(實際上make all的時候竖席,也是使用的這個模板)耘纱。這里,我們要在這個模板的基礎上毕荐,把驅動和相關配置添加進去束析。
在hirootfs_prepare的這里,把
rootfs_scripts/rootfs.tgz
的內(nèi)容解壓到pub這里了憎亚,而這個rootfs.tgz员寇,就是海思提供的rootfs的模板。海思提供的Makefile會把這個rootfs.tgz解壓第美,然后復制到目標位置蝶锋,那么我們就仿照著它,把驅動什往、配置也都復制到目標位置扳缕。把ko文件夾復制到osdrv/rootfs_scripts下。在makefile的第313行添加如下內(nèi)容别威,這樣躯舔,就可以在打包rootfs的時候把驅動打包進去。
有了驅動省古,還需要在系統(tǒng)啟動的時候加載驅動粥庄。所以這里需要設置啟動腳本。解壓rootfs_scripts/rootfs.tgz豺妓,然后把其中的/etc文件夾移動到rootfs_scripts文件夾下惜互。修改etc/init.d/rcS,在文件結尾添加3行琳拭,具體內(nèi)容如下
#! /bin/sh
/bin/mount -a
echo "
_ _ _ _ _ _ _ _ _ _ _ _
\ _ _ _ _ _ ___
/ /__/ \ |_/
/ __ / - _ ___
/ / / / / /
_ _ _ _/ / / \_/ \_ ______
___________\___\__________________
"
for initscript in /etc/init.d/S[0-9][0-9]*
do
if [ -x $initscript ] ;
then
echo "[RCS]: $initscript"
$initscript
fi
done
cd /komod
./load3516dv300 -i -osmem 512 -total 1024
telnetd &
以上三行命令含義為训堆,進入/komod加載驅動,加載時設置DV300板子有1G的內(nèi)存白嘁,其中為系統(tǒng)分配512MB蔫慧,剩余的為MMZ內(nèi)存,最后啟動telnetd服務权薯,這樣可以使用telnet來連接板子。
除了加載驅動睡扬,這時候板子是沒有設置網(wǎng)絡的盟蚣,需要為板子設置一個網(wǎng)絡連接。這里使用的是靜態(tài)IP卖怜。在osdrv/rootfs_scripts文件夾下屎开,修改etc/init.d/S80Network文件。打開這個文件马靠,修改前面幾行即可奄抽。
#!/bin/sh
ipaddr=192.168.1.123
bootp=
gateway=192.168.1.1
netmask=255.255.255.0
hostname=
netdev=eth0
autoconf=
如果想使用/tmp分區(qū)蔼两,則需要在etc/fstab中設置tmp掛載。在osdrv/etc/fstab中逞度,添加這一行
tmpfs /tmp tmpfs defaults,size=20m 0 0
以上配置完成后额划,同樣需要在構建rootfs鏡像時把配置文件添加進去。編輯osdrv/Makefile档泽,在第314行添加如下內(nèi)容:
完成以上內(nèi)容后俊戳,在make hirootfs_prepare的時候就會自動完成相關文件的添加。
5.打包rootfs
在上面馆匿,我們已經(jīng)完成了busybox的修改與編譯抑胎,也完成配置驅動、運行庫渐北、環(huán)境變量等工作了阿逃。這時,使用上面得到的內(nèi)容來打包rootfs赃蛛。
由于上面已經(jīng)在makefile里把重新編譯busybox的內(nèi)容注釋掉了恃锉,在make hibusybox的時候會跳過重新編譯而只是把之前手動編譯的結果復制到目標位置,所以這里我們可以直接使用這個makefile來完成打包焊虏。
在Makefile的第402行左右淡喜,可以找到打包的命令。
在這里诵闭,我們可以發(fā)現(xiàn)炼团,海思提供的makefile編譯rootfs,實際上分了5個步驟:準備疏尿、編譯busybox瘟芝、編譯板子的工具、編譯PC的工具褥琐、打包rootfs锌俱,這個hirootfs_notools_build里,就是調(diào)用mkyaffs2image100之類的工具來打包鏡像敌呈。在上面make all的時候贸宏,已經(jīng)完成了工具的編譯,所以磕洪,我們在第445行附近吭练,仿照hirootfs_build添加一行,讓它不編譯工具析显,只進行復制文件鲫咽、打包的操作:
然后執(zhí)行
make hirootfs_pack
,這樣,就可以看到分尸,我們需要的rootfs鏡像已經(jīng)生成了锦聊。在這一步中,是打包一個文件夾里的文件到rootfs鏡像箩绍,然后這個文件夾里的內(nèi)容又被壓縮到pub/rootfs_glibc.tgz中孔庭,最后這個文件夾被刪掉,可以解壓這個壓縮包看下里面有什么東西伶选。
完成以上步驟后史飞,就得到了海思DV300上使用的rootfs。最后一步仰税,就是把它刷到板子上构资。
0x4. rootfs的刷寫
我買的這個板子,配的是128MB的nand flash陨簇,型號為W25N01G吐绵,頁大小為2K,ECC為4bit河绽。
這個板子的rootfs使用的是yaffs2的分區(qū)格式己单,正好,海思的工具已經(jīng)生成了2k4bit的yaffs格式的rootfs文件耙饰,直接把它刷進去就行纹笼。
刷固件需要使用tftp和串口。在Linux上苟跪,串口可以使用picocom或者minicom廷痘。這里我使用的是picocom。運行
sudo picocom /dev/ttyUSB0 -b 115200
即可打開串口件已。這里tftp使用的是tftpd-hpa笋额。tftp服務器的配置參考這個文章http://blog.chinaunix.net/uid-23065002-id-5203089.html把上面的rootfs文件放到tftpd文件夾下,然后打開串口篷扩,給板子上電兄猩,隨便按鍵進入uboot命令行。執(zhí)行
printenv
鉴未,看下當前uboot的環(huán)境設置枢冤。可以看到,uboot的啟動參數(shù)如圖铜秆,這里有配置啟動的時候nand flash的分區(qū)設置淹真、內(nèi)存劃分、串口終端等信息羽峰,也配置了從flash加載數(shù)據(jù)內(nèi)存的地址和大小。
參考nand命令,可以判斷出梅屉,啟動時從flash中值纱,跳過1MB后讀取了3MB的內(nèi)容到內(nèi)存的0x81000000,這就是加載內(nèi)核坯汤。結合bootargs和bootcmd虐唠,我們知道了flash的rootfs寫入偏移量為1MB+3MB,既0x400000惰聂。
現(xiàn)在可以開始刷寫rootfs疆偿。首先從內(nèi)存中清空一塊空間,用于保存tftp下載的rootfs文件搓幌,這塊空間一定要比下載的rootfs文件大杆故。我這里rootfs是20.7MB,我準備了25MB來存rootfs溉愁。然后再寫入处铛,寫入的時候也是先從nand flash中把相應的空間擦除,然后再寫拐揭。命令如下:
mw.b 81000000 ff 2500000;tftp 0x81000000 rootfs_hi3516dv300_2k_4bit.yaffs2
nand erase 400000 2500000;nand write.yaffs 81000000 400000 14a8400 #注意:14a8400為rootfs文件實際大谐敷 (16進制)
紅色框即為rootfs文件16進制的大小。
使用以上操作即可完成海思平臺的rootfs編譯堂污、驅動的加載家肯、IP配置等操作。有的時候盟猖,如果遇到找不到init導致的kernel panic讨衣,那么,可以把整個nand flash清空扒披,然后重新寫uboot值依、uImage、rootfs碟案,當然愿险,這個操作就比較危險了,如果真的把uboot寫沒了又沒及時恢復進去价说,那么只能用HiTool的HiBurn來寫了辆亏。
每個店家賣的板子都不一樣,我的板子是這個配置的鳖目,當然也有emmc等存儲芯片的板子扮叨,這個就需要參考海思的官方文檔來寫入了,當然领迈,編譯rootfs的方法都是一樣的彻磁。
最后放兩張重新編譯打包的rootfs在板子上跑的截圖