在 i.MX 8QuadXPlus 上使用 Yocto 建置 Linux 系統 4
前言
為了在之後開發過程中不用反覆燒寫 eMMC 與 SD 卡,本篇會設定 U-Boot 載入 Rootfs 以達到我們的目的。
主要內容
寫在前面
我們這次的目標是從 NFS 伺服器中載入 Rootfs。所以我們在開機時,U-Boot 載入的 Kernel 與裝置樹 ( Devicetree ) 仍是 eMMC/SD 卡內的。
在開機的過程中,首先會要把要執行的東西載入到記憶體中執行。 所以我們可以預期,待會我們會需要載入 Kernel 與 裝置樹。 最後才把主控權交給 Kernel。
我們要做的就是在執行時期,修改 U-Boot 傳給 Kernel 的開機參數。
配置 NFS 伺服器
這一個部份先前有筆記過了,如果還沒有看過的同學請參考這裡。
分析 Kernel 與 Devicetree 的載入命令
U-Boot 預設開機在基本的初始化完成後,並且在指定的秒數內沒有都收到使用者的輸入,便會開始執行 bootcmd 中的內容。 我們可以藉由分析它,來初步的知道系統的開機流程。
1print bootcmd
2
3# Output:
4bootcmd=mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if test ${sec_boot} = yes; then if run loadcntr; then run mmcboot; else run netboot; fi; else if run loadimage; then run mmcboot; else run netboot; fi; fi; fi; else booti ${loadaddr} - ${fdt_addr}; fi
整理過後, 我們可以觀察到它真正會執行到的指令為 run loadimage
,接著會是 run mmcboot
1bootcmd=
2 mmc dev ${mmcdev};
3 if mmc rescan; then
4 if run loadbootscript; then
5 run bootscript;
6 else
7 if test ${sec_boot} = yes; then
8 if run loadcntr; then
9 run mmcboot;
10 else
11 run netboot;
12 fi;
13 else
14 if run loadimage; then
15 run mmcboot;
16 else
17 run netboot;
18 fi;
19 fi;
20 fi;
21 else
22 booti ${loadaddr} - ${fdt_addr};
23 fi
從 eMMC 中把 Kernel 載到記憶體裡面
1loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
繼續展開 mmcboot
1mmcboot=
2 echo Booting from mmc ...;
3 run mmcargs;
4 if test ${sec_boot} = yes; then
5 if run auth_os; then
6 run boot_os;
7 else
8 echo ERR: failed to authenticate;
9 fi;
10 else
11 if test ${boot_fdt} = yes || test ${boot_fdt} = try; then
12 if run loadfdt; then
13 run boot_os;
14 else
15 echo WARN: Cannot load the DT;
16 fi;
17 else
18 echo wait for boot;
19 fi;
20 fi;
從 eMMC 中把 裝置樹 ( Devicetree ) 載到記憶體裡面
1loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
開始載入 Kernel ,之後就會把控制權交給 Kernel 了。
1boot_os=booti ${loadaddr} - ${fdt_addr};
echo $?
來看返回值。設定 U-Boot 環境變數
從先前的分析來看,比較重要的指令有:
- loadfdt
- loadimage
- boot_os
所以我們只要進行下列修改,即可。
1setenv serverip "NFS_SERVER_IP"
2setenv rootfs_dir "/srv/rootfs"
3setenv ethaddr "01:02:03:04:05:06"
4setenv image Image
5setenv fdt_file imx8qxp-mek-rpmsg.dtb
6
7setenv rootfsinfo 'setenv bootargs ${bootargs} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${rootfs_dir},v3,tcp'
8setenv bootcmd 'run rootfsinfo; run loadfdt; run loadimage; run boot_os'
9
10saveenv
如我們想還原回預設值,可以使用下列指令
1env default -a
2saveenv
結果
我們可以重新啟動系統,或是直接執行 boot
就可以看到 U-Boot 正在開始執行我們剛才所撰寫的指令了。
會發現,開機的時候有稍微變長了,並且在過程中可以看到 NFS
相關的字樣。
開機完成後,我們可以在 /proc/cmdline
看到我們先前指定的開機參數(bootarg)。

從 NFS 伺服器中載入 rootfs
寫在最後
在 U-Boot 中看到的參數,在本篇並沒有多去探究。不過也別太過傷心,以後我們會專門製作一篇為大家講解。
1loadaddr=0x80280000
2fdt_addr=0x83000000
3image=Image
4fdt_file=imx8qxp-mek-rpmsg.dtb
小結
這次在撰寫本篇時,其實是想把 Kernel 與裝置樹都從網路載下來的。 但礙於筆者的網路環境比較複雜,一直無法成功的藉由 TFTP 傳輸資料。 所以也就暫時作罷。 未來如果有完成這個部份再來更新吧。
( 附上當時的筆記 )
配置 TFTP 伺服器
安裝 TFTP
1sudo aptitude install -y tftpd-hpa
配置分享路徑
1sudo cp /etc/default/tftpd-hpa{,.bk}
2sudo sed -i 's#TFTP_DIRECTORY.*#TFTP_DIRECTORY="/srv/tftp_shared"#g' /etc/default/tftpd-hpa
3sudo sed -i 's#TFTP_ADDRESS.*#TFTP_ADDRESS=":69"#g' /etc/default/tftpd-hpa
4sudo sed -i 's#TFTP_OPTIONS.*#TFTP_OPTIONS="--secure --create"#g' /etc/default/tftpd-hpa
5
6sudo systemctl enable tftpd-hpa
7sudo systemctl restart tftpd-hpa
/etc/default/tftpd-hpa:
1# /etc/default/tftpd-hpa
2TFTP_USERNAME="tftp"
3TFTP_DIRECTORY="/srv/tftp_shared/"
4TFTP_ADDRESS=":69"
5TFTP_OPTIONS="--secure --create"
配置防火牆
1sudo firewall-cmd \
2 --add-rich-rule="rule family='ipv4' source address='192.168.1.2' service name='tftp' accept" \
3 --permanent
4sudo firewall-cmd --reload
除錯
1sudo netstat -nlp
2sudo journalctl -fu tftpd-hpa.service
用戶端測試
在伺服器中建立測試用的資料
1echo "Hello word" > /srv/tftp_shared/hello
在用戶端進行上、下載測試
1echo "I am Groot~~~~~" > groot.txt
2
3tftp SERVER_IP
4tftp get hello
5tftp put groot.txt