小白自制Linux開發板 三. Linux核心與檔案系統移植

淡墨青雲發表於2021-10-09

 上一篇完成了uboot的移植,但是想要愉快的在開發板上玩耍還需要移植Linux核心檔案系統

1.Linux核心

事實上對於F1C100S/F1C200S,Linux官方原始碼已經對licheepi nano進行支援。所以我們完全可以通過licheepi nano的配置檔案進行移植。

1.1. 下載核心原始碼

進入Linux系統官網:

https://www.kernel.org/

這裡面列出的都是一些主要版本,如主線版本,上時間支援版本,個人推薦使用最新的長時間支援版本(5.10.69)。但是因為我這個專案是在參考一位大神的文件的基礎上構建的,所以使用的是5.7.1版本,接下來就給一個選擇其他版本的方式。

選擇任意一項點選 [browse]

在新開啟頁面選擇 【summary】點選【tag】中的【…】切換下載

 

 如果想要直接下載5.7.1版本,請直接使用下面的連線

https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.7.1.tar.gz

下載後完成後,將程式碼複製到Ubuntu虛擬機器並解壓原始碼。

1.2.配置編譯

與上一篇中編譯u-boot一樣,我們也需要配置對Linux編譯進行配置:

  • 指定架構型別
  • 指定交叉編譯工具
  • 專案配置

指定架構,就這個很好理解,就是指定CPU型別,就是配置為Arm就行,交叉編譯工具即為上一篇已經安裝好的編譯工具。

用VS開啟Linux核心程式碼,找到Makefile檔案:

修改如下配置:

ARCH              ?= arm
CROSS_COMPILE     ?=arm-linux-gnueabi-

如果沒找到ARCH 或 CROSS_COMPILE欄位,自己手動新增也行,如下圖所示:

 

 事實上這兩個欄位可以不用指定,在進行make的時候加上對應的引數就行,這裡為了避免麻煩,所以直接放到了makefile檔案中

接下來就是指定專案配置了,這個操作就是讓Linux核心認F1C100S/F1C200S這顆soc。

進入核心原始碼中的arch/arm/configs目錄中,可以看到有很多開發板的配置檔案,其中sunxi_defconfig是全志的配置檔案,但是該配置檔案非常不全,需要額外配置大量的選項,一般選項多大上千個,這裡先使用licheepi_nano的配置檔案。

 https://files.cnblogs.com/files/twzy/linux-licheepi_nano_defconfig.zip

下載該檔案,解壓出linux-licheepi_nano_defconfig,然後將其放到arch/arm/configs/目錄下

 然後通過終端進入Linux-5.7.1根目錄,輸入命令:

make menuconfig

 

 進入圖形配置介面,如圖所示:

該介面和u-boot配置一樣,所以操作方式也是一樣的,上下鍵移動選項,使用空格鍵進行選中或取消選擇,同樣通過空格鍵或Enter鍵,進入子選項配置,通過Tab鍵選擇儲存和退出即可返回上級選單或命令列介面,也可以直接雙擊Esc鍵返回上級目錄。

1.3 配置TF卡裝置樹資訊

我們在完成核心配置後還需要配置TF卡的裝置樹配置,否則即便是能正常執行核心,在載入檔案系統的時候還是會有問題,在這裡配置很簡單:

linux-5.7.1/arch/arm/boot/dts 目錄下,分別修改suniv-f1c100s.dtsisuniv-f1c100s-licheepi-nano.dts 兩個檔案(記住這兩個檔案、以後我們修改的地方多了^_^)

修改suniv-f1c100s.dtsi檔案

在soc->pio 下新增如下程式碼

mmc0_pins: mmc0-pins {
                pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
                function = "mmc0";
            };

soc下新增如下程式碼

mmc0: mmc@1c0f000 {
            compatible = "allwinner,suniv-f1c100s-mmc",
                     "allwinner,sun7i-a20-mmc";
            reg = <0x01c0f000 0x1000>;
            clocks = <&ccu CLK_BUS_MMC0>,
                 <&ccu CLK_MMC0>,
                 <&ccu CLK_MMC0_OUTPUT>,
                 <&ccu CLK_MMC0_SAMPLE>;
            clock-names = "ahb",
                          "mmc",
                          "output",
                          "sample";
            resets = <&ccu RST_BUS_MMC0>;
            reset-names = "ahb";
            interrupts = <23>;
            pinctrl-names = "default";
            pinctrl-0 = <&mmc0_pins>;
            status = "disabled";
            #address-cells = <1>;
            #size-cells = <0>;
        };

如圖,圖中的配置可能與讀者實際內容不一致,這是因為我改了很多東西,忽略即可,只需要關注紅色框中的內容即可。

 修改suniv-f1c100s-licheepi-nano.dts檔案,新增如下程式碼

&mmc0 {
        vmmc-supply = <&reg_vcc3v3>;
        bus-width = <4>;
        broken-cd;
        status = "okay";
};

接下來執行make命令開始編譯核心和裝置樹相關的檔案了

make

首次進行編譯,通常會需要很長時間,編譯完成後,就會在在arch/arm/boot目錄下生成核心檔案:zImage,在arch/arm/boot/dts目錄下裝置樹檔案:suniv-f1c100s-licheepi-nano.dtb  

在編譯過程中,因為所配置Ubuntu系統的差異,可能會因缺少某些元件導致編譯報錯,不要慌,將對應的錯誤關鍵資訊複製到搜尋引擎後安裝即可,一下是作者碰到的兩個編譯報錯,如果有必要可以提前安裝:

 linux-核心編譯配置 lexer.lex.c錯誤

wu@ubuntu:~/linux-5.4.8$ make exynos_defconfig
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
HOSTCC scripts/kconfig/confdata.o
HOSTCC scripts/kconfig/expr.o
LEX scripts/kconfig/lexer.lex.c
/bin/sh: 1: flex: not found
scripts/Makefile.host:9: recipe for target ‘scripts/kconfig/lexer.lex.c’ failed
make[1]: * [scripts/kconfig/lexer.lex.c] Error 127
Makefile:567: recipe for target ‘exynos_defconfig’ failed
make: * [exynos_defconfig] Error 2 

解決方法:

sudo apt-get install bison
sudo apt-get install flex

編譯Linux核心時遇到:“error : openssl/bio.h :No such file or folder”

scripts/extract-cert.c:21:25: fatal error: openssl/bio.h: No such file or directory
compilation terminated.
scripts/Makefile.host:90: recipe for target 'scripts/extract-cert' failed
make[1]: * [scripts/extract-cert] Error 1
Makefile:556: recipe for target 'scripts' failed
make: * [scripts] Error 2

安裝openssl:

sudo apt install libssl-dev

1.4 TF分割槽配置

在上一篇中提到過u-boot 中的bootcmd 配置了Linux核心檔案和裝置樹檔案存放位置,即TF卡的0:1分割槽中,而且我們已經得到了對應的檔案,那我們應該怎麼操作呢。

還記得我們在上一篇中安裝的Gparted軟體嗎,如果不記得,可以通過以下命令安裝:

sudo apt-get install gparted

把需要寫入系統的TF卡插到電腦的USB上,開啟該軟體,可以看到此時有兩個儲存裝置,一個是sda另一個是sdb,其中sdb就是我們的TF卡。如圖:

 

選中sdb,我們可以看到分割槽表中顯示為未分配,對於常規Linux嵌入式系統我們需要分兩個區,一個是存放zImage和dtb檔案,即在bootcmd中配置的0:1分割槽,另一個區存放根檔案系統。對於第一個分割槽,格式為fat16格式,因為u-boot只能識別這個格式,對於第二個區,一般為ext4格式,為Linux核心識別的格式。下面開始分割槽吧。

選中未分配空間並右擊滑鼠,點選[新建],然後填寫相關屬性,然後點選[新增],所示。

 

 

 需要注意【之前的空餘空間】選擇1M,這是給u-boot預留的(u-boot在分割槽表中是無法看到的),【新大小】選擇32M ,【檔案系統】選擇fat16,【卷標】輸入boot。

我們這裡可以用相同的方式新建第二分割槽——ext4分割槽,如下圖

 這裡我們設定為100M,檔案系統為ext4,卷標為rootfs,然後新增新增。

配置好分割槽表後,點選工具來中的【對鉤】使配置的分割槽表生效。

配置分割槽完畢後,我們就可以在檔案管理器中看到掛載的兩個分割槽,如圖

1.5 核心複製與執行

那麼,我們將剛才生成好的zImage和dtb檔案複製到TF卡的BOOT分割槽中

退出TF卡,插入開發板,上電,按重啟,我們就可以看到u-boot啟動完成後,自動進入了核心啟動環節,但是啟動後一會就報錯了,因為掛載檔案系統產生錯誤。

U-Boot SPL 2018.01-05679-g013ca457fd-dirty (Sep 28 2021 - 15:29:32)
DRAM: 32 MiB
Trying to boot from MMC1


U-Boot 2018.01-05679-g013ca457fd-dirty (Sep 28 2021 - 15:29:32 +0800) Allwinner Technology

CPU:   Allwinner F Series (SUNIV)
Model: Snail Card
DRAM:  32 MiB
MMC:   SUNXI SD/MMC: 0
*** Warning - bad CRC, using default environment

In:    serial@1c25000
Out:   serial@1c25000
Err:   serial@1c25000
Net:   No ethernet found.
starting USB...
No controllers found
Hit any key to stop autoboot:  0 
reading zImage
4515448 bytes read in 231 ms (18.6 MiB/s)
reading suniv-f1c100s-licheepi-nano.dtb
6105 bytes read in 26 ms (228.5 KiB/s)
## Flattened Device Tree blob at 80c00000
   Booting using the fdt blob at 0x80c00000
   Loading Device Tree to 816fb000, end 816ff7d8 ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.7.1 (twzy@ubuntu) (gcc version 7.2.1 20171011 (Linaro GCC 7.2-2017.11), GNU ld (Linaro_Binutils-2017.11) 2.28.2.20170706) #55 Tue Sep 28 21:04:24 CST 2021
[    0.000000] CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=0005317f
[    0.000000] CPU: VIVT data cache, VIVT instruction cache
[    0.000000] OF: fdt: Machine model: LinuxCard by Kevin
[    0.000000] Memory policy: Data cache writeback
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 8128
[    0.000000] Kernel command line: console=tty0 console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2  rw
[    0.000000] Dentry cache hash table entries: 4096 (order: 2, 16384 bytes, linear)
[    0.000000] Inode-cache hash table entries: 2048 (order: 1, 8192 bytes, linear)
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 21496K/32768K available (7168K kernel code, 403K rwdata, 1664K rodata, 1024K init, 246K bss, 11272K reserved, 0K cma-reserved, 0K highmem)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1

 …………………

[    6.598874] Run /etc/init as init process
[    6.603993] Run /bin/init as init process
[    6.609078] Run /bin/sh as init process
[    6.613763] Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance.
[    6.629985] CPU: 0 PID: 1 Comm: swapper Not tainted 5.7.1 #55
[    6.636727] Hardware name: Allwinner suniv Family
[    6.642216] [<c010d604>] (unwind_backtrace) from [<c010ab60>] (show_stack+0x10/0x14)
[    6.651031] [<c010ab60>] (show_stack) from [<c01165a4>] (panic+0xe8/0x2e4)
[    6.658951] [<c01165a4>] (panic) from [<c071d080>] (kernel_init+0xd8/0x110)
[    6.666960] [<c071d080>] (kernel_init) from [<c0100140>] (ret_from_fork+0x14/0x34)
[    6.675547] Exception stack(0xc1835fb0 to 0xc1835ff8)
[    6.681293] 5fa0:                                     00000000 00000000 00000000 00000000
[    6.690714] 5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[    6.700102] 5fe0: 00000000 00000000 00000000 00000000 00000013 00000000
[    6.707633] Rebooting in 5 seconds..
[   12.687513] Reboot failed -- System halted

核心移植就基本結束了,要想讓小板真正的執行起來,還需要Linux的檔案系統,那開始吧。

 

2.檔案系統移植

 根檔案系統(rootfs)是核心啟動後掛載的第一個檔案系統,如果沒有根檔案系統,核心將無法開啟shell以及其他程式。

實際上核心啟動後會先掛載一個虛擬的檔案系統,這個虛擬檔案系統是在記憶體中執行的,其主要執行核心程式,虛擬檔案系統掛載之後才掛載硬碟(TF卡或者emmc)上的根檔案系統。

製作檔案系統也有很多方式,如通過busyBox、Buildroot等工具製作。

本次使用Buildroot,製作過程相對簡單,相容性好,由於根檔案系統製作比較簡單。

進入buildroot官網

https://buildroot.org/downloads

這裡選擇buildroot2018.2.11版本,將下載好軟體包傳入Ubuntu系統中,然後解壓並進入原始碼目錄中,輸入清理命令。主要用於初始化一些設定,命令如下:

make clean

然後輸入以下命令進入配置介面

make menuconfig

此時會終端進入圖形配置介面,如圖:

2.1 Target options配置

先選擇Target options選項,進行對應晶片soc相關的配置,如圖:

 

配置如圖所示,下面是對其的解釋

  • 第一個選項為架構選擇,這裡選擇ARM架構小端模式,
  • 第二個為輸出的二進位制檔案格式,這裡選擇EFL格式,
  • 第三個為架構體系,這裡選擇arm926t,因為F1C200S/F1C100S的架構就是這個架構,
  • 第四個為向量浮點處理器,這裡不勾選,因為對於F1C200S/F1C100S而言,其內部沒有浮點運算單元,只能進行軟浮點運算,也就是模擬浮點預運算。
  • 第五個為應用程式二進位制介面,這裡選擇EABI,原因是該格式支援軟體浮點和硬體實現浮點功能混用。
  • 第六個為浮點運算規則,這裡使用軟體浮點
  • 第七個選擇指令集,這裡選擇ARM指令集,因為thumb主要針對Cortex M系列而言的,對於執行作業系統的A系列以及ARM9和ARM11而言,使用的都是32位的ARM指令集。

按【Tab鍵】選擇<save>進行儲存,按【Esc鍵】回到上一級配置介面。

2.2 Build options配置

進入第二個Build options選項,配置如圖

 

按T【ab鍵】選擇<save>進行儲存,按【Esc鍵】回到上一級配置介面。

2.3 Toolchain配置

進入第三個Toolchain選項,配置如圖:

這裡我們選擇一些C\C++相關的庫,這樣我們就可以在開發板上直接編譯程式了,儲存返回。

2.4 System configuration配置

對於System configuration選項,這裡主要是配置一些系統登入時候顯示的內容,配置如圖

這裡主要配置了登入時候顯示的內容和root賬號登入密碼,接下來儲存配置並且退回到命令列介面。

然後執行構建檔案系統命令:

make

因為是首次編譯,而且buildroot在製作檔案系統的時候需要聯網獲取元件,所以會編譯很久,那麼“去和妲己玩耍吧”

當你終於被別人坑的自閉的時候,檔案系統大概也許可能已經編譯完畢了。

2.5 檔案系統移植與執行

此時在原始碼的output/images目錄下有一個rootfs.tar,這個檔案就是最終生成的根檔案系統映象,現在只需要將該映象解壓到TF卡的第二分割槽即可。插入TF卡到電腦端,進入out/images目錄,然後輸入

# sudo tar -xvf rootfs.tar -C /media/<你的使用者名稱>/rootfs/
# 墨雲的賬號是twzy
sudo tar -xvf rootfs.tar -C /media/twzy/rootfs/

此時可以看到TF卡的rootfs分割槽中有檔案系統了

插入開發板,連線好串列埠,開啟串列埠助手或者其他串列埠終端軟體,可以看到根檔案系統成功掛載,同時進入shell互動,使用者名稱預設為root,密碼:123456,進入root賬號後

那麼恭喜,你已經擁有了自己的Linux發行版。

至此我們完成了全部的系統移植任務,從下一篇開始我們將會升級我們的硬體裝置和做一些更加有意義的東西,期待嗎?

2.6 升級逼格

我們發現登入進自制的Linux系統後,命令列前置無論怎樣只顯示一個#號,逼格略低呀,怎麼處理呢?

修改/etc/profile檔案

vi /etc/profile

寫入

export PS1='[\u@\h: \w\a\]$'

重啟小板,就可以看到與與常規Linux一樣的操作體驗了,只是root賬號的時候還是顯示 $ 符號

需要注意的是,在開發板執行過程中,如果想要重啟,請先執行

poweroff

命令正常關閉系統後,在按重啟按鈕,否則有很大概率回造成檔案系統損壞。

3. 點個燈吧

還記得我們在第一篇中提到過的我們自制小開發板的唯一的那個外設——LED燈嗎?

那我們就利用Linux提供的GPIO系統通過shell命令進行點燈實驗吧。

我們首先需要回到檔案系統製作選單

Device Drivers -> 
        GPIO Support ->
                /sys/class/gpio/… (sysfs interface)。

按如下方式進行配置,然後編譯完rootfs,重新寫入小板

 

通過硬體可知LED燈連線的是PE6介面,低電平亮燈

這裡我們先要了解一下GPIO編號和值的計算方式

引腳編號 = 控制引腳的暫存器基數 + 控制引腳暫存器位數
批註:
引腳編號是gpiochipxxx下的base + 第幾個GPIO,也就是base加偏移,偏移的是位數。
例如gpiochip34 下的第1個GPIO那麼編號就是34 + 1 = 35

對於F1C200S/F1C100S這裡:A=0、B=1、C=2D=3、E=4  ……、32是固定值、6就是偏移量

舉個例子(如果使想用 PE6,那麼引腳編號就可能等於 4 x 32 + 6 = 134。

這是一些參考命令

# 1、匯出
echo 134 > /sys/class/gpio/export
# 2、設定方向
echo out > /sys/class/gpio/gpio134/direction
# 3、檢視方向
cat /sys/class/gpio/gpio134/direction
# 4、設定輸出(對於LED 設定1 為高電平即LED燈滅,設定0 為低電平,LED燈亮)
echo 1 > /sys/class/gpio/gpio134/value
# 5、檢視輸出值
cat /sys/class/gpio/gpio134/value
# 6、取消匯出
echo 134 > /sys/class/gpio/unexport

輸入如下命令:

echo 134 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio134/direction # 燈亮 (預設設定為高電平)
echo 0 > /sys/class/gpio/gpio134/value        # 燈滅 
echo 1 > /sys/class/gpio/gpio134/value        # 燈亮

echo 134 > /sys/class/gpio/unexport

效果如下:

 我們這裡藉助Linux內建的GPIO子系統進行了電燈實驗,但是真正Linux靈魂點燈是要通過驅動方式來實現的,但是誰讓我是小白呢,以後再說吧。

相關文章