目录
一、根文件系统介绍
二、根文件系统移植
1、buildroot下载
2、根文件系统制作
3、根文件系统移植
4、根文件系统加载
5、mmc设备问题分析
6、mmc功能开启
7、设备树编译与下载
三、参考内容
文件系统是对一个存储设备上的数据和元数据进行组织的机制,这种机制有利于用户和操作系统的交互。在Linux没有文件系统的话,用户和操作系统的交互也就断开了。
Unix没有盘符的概念,要求自己的文件系统是单一的一棵树。直接挂载在整棵树根上的那个盘里面的文件系统,就是根文件系统。
文件系统不是指某一个文件系统类型,而是指任何可以将文件目录组织成特定系统所需结构的文件系统,然后把它挂载在系统的根上。
Linux中的根文件系统像是一个文件夹管理系统,或者说像是一本书的目录,在某种意义上讲,根文件系统就是一个文件夹,只不过是这是一个特殊的文件夹,在这个目录里面会有很多的子目录,如下图所示。
下面是虚拟机中Ubuntu系统的根文件系统,根文件系统的目录名字为 ‘/’ ,也就是一个斜杠,各个目录的功能在下面的表格中做了详细介绍。
Item | Description |
---|---|
/ (root filesystem) | 根文件系统是文件系统的最高级别目录。在其他文件系统被挂载之前,它必须包含启动Linux系统所需的所有文件。它必须包括启动其余文件系统所需的所有可执行文件和库。在系统启动后,所有其他文件系统都被挂载在标准的、定义明确的挂载点上,作为根文件系统的子目录。 |
/bin | /bin目录包含用户可执行文件。 |
/boot | 包含启动Linux计算机所需的静态引导程序和内核可执行文件以及配置文件。 |
/dev | 这个目录包含了连接到系统的每个硬件设备的设备文件。这些不是设备驱动程序,而是代表计算机上每个设备的文件,并为访问这些设备提供便利。 |
/etc | 包含主机的本地系统配置文件。 |
/home | 用户文件的主目录存储。每个用户都有一个/home的子目录。 |
/lib | 包含启动系统所需的共享库文件。 |
/media | 一个安装外部可移动媒体设备的地方,如可能连接到主机的USB拇指驱动器。 |
/mnt | 普通文件系统(如不是可移动媒体)的临时挂载点,可以在管理员修复或处理文件系统的时候使用。 |
/opt | 可选的文件,如供应商提供的应用程序应位于这里。 |
/root | 这不是根(/)文件系统。它是根用户的主目录。 |
/sbin | 系统二进制文件。这些是用于系统管理的可执行文件。 |
/tmp | 临时目录。由操作系统和许多程序用来存储临时文件。用户也可以在这里临时存储文件。请注意,存储在这里的文件可能在任何时候被删除,而无需事先通知。 |
/usr | 这些是可共享的、只读的文件,包括可执行的二进制文件和库、人文件和其他类型的文件。 |
/var | 可变的数据文件被存储在这里。这可以包括像日志文件、MySQL和其他数据库文件、网络服务器数据文件、电子邮件收件箱,以及更多的东西。 |
我们使用buildroot制作根文件系统,之前imx6ull使用的busybox,这里换一下,哈哈,首先进入官网,下载根文件系统buildroot2018.2.11版本,与墨云保持一致,下载后放到Ubuntu中解压。https://buildroot.org/download.htmlhttps://buildroot.org/download.html
这个网站比较友好,不需要魔法,buildroot体积较小,直接下载即可,下面是几个不同后缀的文件。
上面的四个选项,下载后是下面的文件,sign是什么文件我不清楚,嘿嘿,欢迎评论区指出,选择一个压缩包,到Ubuntu中解压
解压完成后进入根目录,清理工程后,进入图形界面配置:
make clean
make menuconfig
图形配置界面如下图所示,使用方向键选择不同选项,空格进行选中,回车进行确认。
Target options选项的配置如下图所示,每个选项的含义墨云解释过了,我就不造轮子了。
- 第一个选项为架构选择,这里选择ARM架构小端模式,
- 第二个为输出的二进制文件格式,这里选择EFL格式,
- 第三个为架构体系,这里选择arm926t,因为F1C200S/F1C100S的架构就是这个架构,
- 第四个为矢量浮点处理器,这里不勾选,因为对于F1C200S/F1C100S而言,其内部没有浮点运算单元,只能进行软浮点运算,也就是模拟浮点预运算。
- 第五个为应用程序二进制接口,这里选择EABI,原因是该格式支持软件浮点和硬件实现浮点功能混用。
- 第六个为浮点运算规则,这里使用软件浮点
- 第七个选择指令集,这里选择ARM指令集,因为thumb主要针对Cortex M系列而言的,对于运行操作系统的A系列以及ARM9和ARM11而言,使用的都是32位的ARM指令集。
来自:小白自制Linux开发板 三. Linux内核与文件系统移植 - 淡墨青云 - 博客园
据说Toolchain按照下方配置(打开了一些功能)可以在开发板上直接编译程序。
登录的时候会显示 “Wecome to kashine linux system.” ,并且我们设置了root用户密码为“good luck!”。
配置完成后,使用make命令进行编译,从下面可以看出,buildroot的编译需要网络支持,以通过网络配置我们选择的内容。 当然如果你的虚拟机无法连接网络,请看虚拟机ubuntu桥接怎么联网,或者是虚拟机net模式访问互联网。
此处是漫长的等待..........,建议来一道力扣中等!复杂链表的复制!力扣https://leetcode.cn/problems/fu-za-lian-biao-de-fu-zhi-lcof/?envType=study-plan&id=lcof&plan=lcof&plan_progress=y16046r
这个下载速度真的是奇慢无比!如果下载依赖包或者软件速度非常慢,可以尝试(不要试了,我试过不管用,哈哈)Ubuntu20.04换源之后依旧慢?Ubuntu16.04找不到Software&Updates(软件更新),但我试了一下仍然很慢,这不科学呀!魔法也不管用。
终于编译完成了!已经是第二天了,昨天搞到1:00还没有编译完,我就直接挂起虚拟机,今天早晨打开Ubuntu没大会,就已经编译完成了,大概两个多小时。编译完成后,在buildroot根目录的output/images目录下生成一个rootfs.rar文件,这个就是我们心心念念的根文件系统。
注意:第二次编译就会快很多,比如误删,别问我怎么知道的,唔哈哈哈。
将上面得到的rootfs.rar解压到TF卡的第二个分区,也就是rootfs分区,不要解压完成在复制过去,因为解压出来好多文件夹。两种选择,要么把压缩包复制到rootfs分区,解压后删除压缩包,要么直接解压到第二分区。使用如下命令将压缩包解压到rootfs分区后如下图所示:
sudo tar -vxf rootfs.tar -C /media/project01/rootfs/
其中tar解压缩命令格式如下:
选项 | 含义 |
-x | 对 tar 包做解打包操作。 |
-f | 指定要解压的 tar 包的包名。 |
-t | 只查看 tar 包中有哪些文件或目录,不对 tar 包做解打包操作。 |
-C 目录 | 指定解打包位置。 |
-v | 显示解打包的具体过程。 |
TF卡第二个分区内已经放置了我们制作好的根文件系统,将TF卡插到开发板上并上电启动,打开串口调试助手,设置波特率115200,可以看到下面的内容,uboot和内核启动成功。
但是并没有启动Linux的根文件系统,我们看最后打印出的提示代码为: Waiting for root device /dev/mmcblk0p2...,看来是mmcblk0p2表示mmc设备(MultiMedia Card,多媒体存储卡)没有被内核启动成功,mmcblk0是我们的TF卡,p2代表第二个分区,我们最终确定是mmc设备出了问题。
我们先来分析一下出现这个问题的原因,如果说mmcblk0p2设备出现问题,那么为什么在启动Linux内核的时候没有出错的,内核文件和设备树文件存放在mmcblk0p1中,如果是mmcblk0p2加载出错,也就表示TF卡的设备树或者是驱动出了问题,那么mmcblk0p1不应该也是错的吗?那Linux内核不应该能够启动才对的?
小朋友,你是否有很多问号?好的,现在来说一下我对上面问题的理解。其实上面的问题有点不太对,不知道大家是否有相同的疑问,所以我对上面也进行了保留。下面分析:首先是uboot的启动,在TF卡的8k处,我们硬件没有问题,也就是说TF卡接在F1C200s的指定引脚,bootROM启动的时候一定会读取TF卡的8k位置,成功启动uboot,而在uboot启动后,进入倒计时,倒计时结束后,执行bootcmd命令,bootcmd命令如下所示:
load mmc 0:1 0x80008000 zImage;load mmc 0:1 0x80c08000 suniv-f1c100s-licheepi-nano.dtb;bootz 0x80008000 - 0x80c08000;
该命令的主要作用是将zImage和设备树从mmc的第一个分区拷贝到内存中执行,这个应该好理解哈,Linux内核启动成功了,说明uboot能够将两个文件拷贝到内存中执行,进一步说明uboot的TF卡驱动是没有问题的,拷贝结束后,uboot生命周期结束,Linux内核启动,但是Linux内核TF卡启动出错,导致无法加载TF卡第二分区中的根文件系统。
至此,我们大致分析出导致无法启动Linux根文件系统的原因是:TF卡设备树或者驱动出错。对于设备树来讲,我们只需要提供对应的硬件信息,即可在不同的开发板可以使用相同的驱动。形象一点讲就是:对某个LED点灯程序(驱动),我宏定义了一个LED管脚(设备树),程序是官方给的,也就是说确定这个程序可以点灯,现在我们将这套程序放到另一个相同主控的开发板上运行,出问题的话就很可能是我们的宏定义出错了,也就是说,很可能是设备树出错。
通过上面的分析,我们怀疑很可能是设备树出了问题,那下面我们对设备树进行检查。首先介绍一下设备树相关文件,打开/arch/arm/boot/dts可以看到我们我们前面拷贝到TF卡KERNEL分区的设备树文件,其中.dtsi文件为“头文件”,储存一个主控芯片的共同信息,供针对相同主控芯片不同开发板的设备树源文件.dts文件调用,编译后生成设备树文件.dtb。
打开/arch/arm/boot/dts目录下的suniv-f1c100s-licheepi-nano.dts、suniv-f1c100s.dtsi,在dtsi文件中并没有发现mmc的内容,也就是如orange2c所说,是因为我们使用的主线Linux内核源码,而主线Linux设备树中并没有开启mmc。
参考荔枝派设备树源码,进行以下修改。首先添加头文件:
#include
#include
然后在soc结点下的pio添加如下代码,其中PF0、PF1、PF2、PF3、PF4、PF5为TF卡相关引脚,详见https://blog.csdn.net/qq_41709234/article/details/124389957。
mmc0_pins: mmc0-pins {
pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
function = "mmc0";
};
在soc结点下添加如下代码,使用pinctrl子系统初始化引脚,其中compatible变量保存着mmc对应的驱动。其他时钟总线之类的我就不详细解释了,因为我也不懂,哈哈。但是一定要注意,.dtsi文件里面是默认关闭mm功能的(可以看到下面的status是disabled状态),需要在.dts里面使能。
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>;
};
.dtsi文件修改完成后如下所示:
// SPDX-License-Identifier: (GPL-2.0+ OR X11)
/*
* Copyright 2018 Icenowy Zheng
* Copyright 2018 Mesih Kilinc
*/
// modify by kashine
#include
#include
/ {
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
clocks {
osc24M: clk-24M {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <24000000>;
clock-output-names = "osc24M";
};
osc32k: clk-32k {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <32768>;
clock-output-names = "osc32k";
};
};
cpus {
cpu {
compatible = "arm,arm926ej-s";
device_type = "cpu";
};
};
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
sram-controller@1c00000 {
compatible = "allwinner,suniv-f1c100s-system-control",
"allwinner,sun4i-a10-system-control";
reg = <0x01c00000 0x30>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
sram_d: sram@10000 {
compatible = "mmio-sram";
reg = <0x00010000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x00010000 0x1000>;
otg_sram: sram-section@0 {
compatible = "allwinner,suniv-f1c100s-sram-d",
"allwinner,sun4i-a10-sram-d";
reg = <0x0000 0x1000>;
status = "disabled";
};
};
};
ccu: clock@1c20000 {
compatible = "allwinner,suniv-f1c100s-ccu";
reg = <0x01c20000 0x400>;
clocks = <&osc24M>, <&osc32k>;
clock-names = "hosc", "losc";
#clock-cells = <1>;
#reset-cells = <1>;
};
intc: interrupt-controller@1c20400 {
compatible = "allwinner,suniv-f1c100s-ic";
reg = <0x01c20400 0x400>;
interrupt-controller;
#interrupt-cells = <1>;
};
pio: pinctrl@1c20800 {
compatible = "allwinner,suniv-f1c100s-pinctrl";
reg = <0x01c20800 0x400>;
interrupts = <38>, <39>, <40>;
clocks = <&ccu 37>, <&osc24M>, <&osc32k>;
clock-names = "apb", "hosc", "losc";
gpio-controller;
interrupt-controller;
#interrupt-cells = <3>;
#gpio-cells = <3>;
uart0_pe_pins: uart0-pe-pins {
pins = "PE0", "PE1";
function = "uart0";
};
// modify by kashine
mmc0_pins: mmc0-pins {
pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
function = "mmc0";
};
};
timer@1c20c00 {
compatible = "allwinner,suniv-f1c100s-timer";
reg = <0x01c20c00 0x90>;
interrupts = <13>;
clocks = <&osc24M>;
};
wdt: watchdog@1c20ca0 {
compatible = "allwinner,suniv-f1c100s-wdt",
"allwinner,sun4i-a10-wdt";
reg = <0x01c20ca0 0x20>;
};
uart0: serial@1c25000 {
compatible = "snps,dw-apb-uart";
reg = <0x01c25000 0x400>;
interrupts = <1>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu 38>;
resets = <&ccu 24>;
status = "disabled";
};
uart1: serial@1c25400 {
compatible = "snps,dw-apb-uart";
reg = <0x01c25400 0x400>;
interrupts = <2>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu 39>;
resets = <&ccu 25>;
status = "disabled";
};
uart2: serial@1c25800 {
compatible = "snps,dw-apb-uart";
reg = <0x01c25800 0x400>;
interrupts = <3>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&ccu 40>;
resets = <&ccu 26>;
status = "disabled";
};
// modify by kashine
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>;
};
};
};
在.dts文件里面进行以下修改,首先使能mmc:
&mmc0 {
vmmc-supply = <®_vcc3v3>;
bus-width = <4>;
broken-cd;
status = "okay";
};
然后这个是墨云在根节点下添加的一个结点,他并没有说明为什么添加,我百度了一下,这是一个电源管理相关的结点,使用regulator-fixed来实现使用GPIO控制某个电源开关 ,希望在开机时尽快输出高低电平来控制电源。详见。Rockchip RK3588 kernel dts解析之regulator-fixed。
reg_vcc3v3: vcc3v3 {
compatible = "regulator-fixed";
regulator-name = "vcc3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
.dts文件修改完成之后下所示:
// SPDX-License-Identifier: (GPL-2.0+ OR X11)
/*
* Copyright 2018 Icenowy Zheng
*/
/dts-v1/;
#include "suniv-f1c100s.dtsi"
/ {
model = "Lichee Pi Nano";
compatible = "licheepi,licheepi-nano", "allwinner,suniv-f1c100s";
aliases {
serial0 = &uart0;
};
chosen {
stdout-path = "serial0:115200n8";
};
// modify by kashine
reg_vcc3v3: vcc3v3 {
compatible = "regulator-fixed";
regulator-name = "vcc3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
};
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_pe_pins>;
status = "okay";
};
// modify by kashine
&mmc0 {
vmmc-supply = <®_vcc3v3>;
bus-width = <4>;
broken-cd;
status = "okay";
};
在Linux内核根目录下,使用以下命令重新编译设备树,内核无需重新编译。
make dtbs
在内核源码根目录使用make dtbs编译变成后生成设备树文件,如下图所示。
编译完成后,将新的设备树文件(.dtb)拷贝到TF卡的第二分区,zImage无需修改,打开串口调试助手,上电复位,成功启动根文件系统!!!,成功打印出“Welcome to kashine linux system.”,root为管理员用户名,密码为前面设置的“good luck!”,将路径切换到根目录之后,查看根目录的文件夹,如下图所示。
在上电成功启动根文件系统之后,不要直接拔掉USB断电,可能造成根文件系统损坏,使用poweroff命令关闭根文件系统后断电。
1. 墨云uboot移植;
2. 正点原子《嵌入式Linux驱动开发指南》