当前位置:   article > 正文

(四)根文件系统--从零开始自制linux掌上电脑(F1C200S)<嵌入式项目>

从零开始自制linux掌上电脑

目录

一、根文件系统介绍

二、根文件系统移植

1、buildroot下载

2、根文件系统制作

3、根文件系统移植

4、根文件系统加载

5、mmc设备问题分析

6、mmc功能开启

7、设备树编译与下载

三、参考内容


一、根文件系统介绍

文件系统是对一个存储设备上的数据和元数据进行组织的机制,这种机制有利于用户和操作系统的交互。在Linux没有文件系统的话,用户和操作系统的交互也就断开了。

Unix没有盘符的概念,要求自己的文件系统是单一的一棵树。直接挂载在整棵树根上的那个盘里面的文件系统,就是根文件系统。

文件系统不是指某一个文件系统类型,而是指任何可以将文件目录组织成特定系统所需结构的文件系统,然后把它挂载在系统的上。

Linux中的根文件系统像是一个文件夹管理系统,或者说像是一本书的目录,在某种意义上讲,根文件系统就是一个文件夹,只不过是这是一个特殊的文件夹,在这个目录里面会有很多的子目录,如下图所示。

下面是虚拟机中Ubuntu系统的根文件系统,根文件系统的目录名字为 ‘/’ ,也就是一个斜杠,各个目录的功能在下面的表格中做了详细介绍。

ItemDescription
/ (root filesystem)根文件系统是文件系统的最高级别目录。在其他文件系统被挂载之前,它必须包含启动Linux系统所需的所有文件。它必须包括启动其余文件系统所需的所有可执行文件和库。在系统启动后,所有其他文件系统都被挂载在标准的、定义明确的挂载点上,作为根文件系统的子目录。
/bin/bin目录包含用户可执行文件。
/boot包含启动Linux计算机所需的静态引导程序和内核可执行文件以及配置文件。
/dev这个目录包含了连接到系统的每个硬件设备的设备文件。这些不是设备驱动程序,而是代表计算机上每个设备的文件,并为访问这些设备提供便利。
/etc包含主机的本地系统配置文件。
/home用户文件的主目录存储。每个用户都有一个/home的子目录。
/lib包含启动系统所需的共享库文件。
/media一个安装外部可移动媒体设备的地方,如可能连接到主机的USB拇指驱动器。
/mnt普通文件系统(如不是可移动媒体)的临时挂载点,可以在管理员修复或处理文件系统的时候使用。
/opt可选的文件,如供应商提供的应用程序应位于这里。
/root这不是根(/)文件系统。它是根用户的主目录。
/sbin系统二进制文件。这些是用于系统管理的可执行文件。
/tmp临时目录。由操作系统和许多程序用来存储临时文件。用户也可以在这里临时存储文件。请注意,存储在这里的文件可能在任何时候被删除,而无需事先通知。
/usr这些是可共享的、只读的文件,包括可执行的二进制文件和库、人文件和其他类型的文件。
/var可变的数据文件被存储在这里。这可以包括像日志文件、MySQL和其他数据库文件、网络服务器数据文件、电子邮件收件箱,以及更多的东西。

二、根文件系统移植

1、buildroot下载

我们使用buildroot制作根文件系统,之前imx6ull使用的busybox,这里换一下,哈哈,首先进入官网,下载根文件系统buildroot2018.2.11版本,与墨云保持一致,下载后放到Ubuntu中解压。https://buildroot.org/download.htmlhttps://buildroot.org/download.html

这个网站比较友好,不需要魔法,buildroot体积较小,直接下载即可,下面是几个不同后缀的文件。

 上面的四个选项,下载后是下面的文件,sign是什么文件我不清楚,嘿嘿,欢迎评论区指出,选择一个压缩包,到Ubuntu中解压

2、根文件系统制作

 解压完成后进入根目录,清理工程后,进入图形界面配置:

  1. make clean
  2. 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文件这个就是我们心心念念的根文件系统。

注意:第二次编译就会快很多,比如误删,别问我怎么知道的,唔哈哈哈。 

 3、根文件系统移植

将上面得到的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显示解打包的具体过程。

4、根文件系统加载

TF卡第二个分区内已经放置了我们制作好的根文件系统,将TF卡插到开发板上并上电启动,打开串口调试助手,设置波特率115200,可以看到下面的内容,uboot和内核启动成功。

但是并没有启动Linux的根文件系统,我们看最后打印出的提示代码为: Waiting for root device /dev/mmcblk0p2...,看来是mmcblk0p2表示mmc设备(MultiMedia Card,多媒体存储卡)没有被内核启动成功,mmcblk0是我们的TF卡,p2代表第二个分区,我们最终确定是mmc设备出了问题。

 5、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。

6、mmc功能开启

参考荔枝派设备树源码,进行以下修改。首先添加头文件:

  1. #include <dt-bindings/clock/suniv-ccu-f1c100s.h>
  2. #include <dt-bindings/reset/suniv-ccu-f1c100s.h>

然后在soc结点下的pio添加如下代码,其中PF0、PF1、PF2、PF3、PF4、PF5为TF卡相关引脚,详见https://blog.csdn.net/qq_41709234/article/details/124389957

  1. mmc0_pins: mmc0-pins {
  2. pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
  3. function = "mmc0";
  4. };

在soc结点下添加如下代码,使用pinctrl子系统初始化引脚,其中compatible变量保存着mmc对应的驱动。其他时钟总线之类的我就不详细解释了,因为我也不懂,哈哈。但是一定要注意,.dtsi文件里面是默认关闭mm功能的(可以看到下面的status是disabled状态),需要在.dts里面使能。

  1. mmc0: mmc@1c0f000 {
  2. compatible = "allwinner,suniv-f1c100s-mmc",
  3. "allwinner,sun7i-a20-mmc";
  4. reg = <0x01c0f000 0x1000>;
  5. clocks = <&ccu CLK_BUS_MMC0>,
  6. <&ccu CLK_MMC0>,
  7. <&ccu CLK_MMC0_OUTPUT>,
  8. <&ccu CLK_MMC0_SAMPLE>;
  9. clock-names = "ahb",
  10. "mmc",
  11. "output",
  12. "sample";
  13. resets = <&ccu RST_BUS_MMC0>;
  14. reset-names = "ahb";
  15. interrupts = <23>;
  16. pinctrl-names = "default";
  17. pinctrl-0 = <&mmc0_pins>;
  18. status = "disabled";
  19. #address-cells = <1>;
  20. #size-cells = <0>;
  21. };

 .dtsi文件修改完成后如下所示:

  1. // SPDX-License-Identifier: (GPL-2.0+ OR X11)
  2. /*
  3. * Copyright 2018 Icenowy Zheng <icenowy@aosc.io>
  4. * Copyright 2018 Mesih Kilinc <mesihkilinc@gmail.com>
  5. */
  6. // modify by kashine
  7. #include <dt-bindings/clock/suniv-ccu-f1c100s.h>
  8. #include <dt-bindings/reset/suniv-ccu-f1c100s.h>
  9. / {
  10. #address-cells = <1>;
  11. #size-cells = <1>;
  12. interrupt-parent = <&intc>;
  13. clocks {
  14. osc24M: clk-24M {
  15. #clock-cells = <0>;
  16. compatible = "fixed-clock";
  17. clock-frequency = <24000000>;
  18. clock-output-names = "osc24M";
  19. };
  20. osc32k: clk-32k {
  21. #clock-cells = <0>;
  22. compatible = "fixed-clock";
  23. clock-frequency = <32768>;
  24. clock-output-names = "osc32k";
  25. };
  26. };
  27. cpus {
  28. cpu {
  29. compatible = "arm,arm926ej-s";
  30. device_type = "cpu";
  31. };
  32. };
  33. soc {
  34. compatible = "simple-bus";
  35. #address-cells = <1>;
  36. #size-cells = <1>;
  37. ranges;
  38. sram-controller@1c00000 {
  39. compatible = "allwinner,suniv-f1c100s-system-control",
  40. "allwinner,sun4i-a10-system-control";
  41. reg = <0x01c00000 0x30>;
  42. #address-cells = <1>;
  43. #size-cells = <1>;
  44. ranges;
  45. sram_d: sram@10000 {
  46. compatible = "mmio-sram";
  47. reg = <0x00010000 0x1000>;
  48. #address-cells = <1>;
  49. #size-cells = <1>;
  50. ranges = <0 0x00010000 0x1000>;
  51. otg_sram: sram-section@0 {
  52. compatible = "allwinner,suniv-f1c100s-sram-d",
  53. "allwinner,sun4i-a10-sram-d";
  54. reg = <0x0000 0x1000>;
  55. status = "disabled";
  56. };
  57. };
  58. };
  59. ccu: clock@1c20000 {
  60. compatible = "allwinner,suniv-f1c100s-ccu";
  61. reg = <0x01c20000 0x400>;
  62. clocks = <&osc24M>, <&osc32k>;
  63. clock-names = "hosc", "losc";
  64. #clock-cells = <1>;
  65. #reset-cells = <1>;
  66. };
  67. intc: interrupt-controller@1c20400 {
  68. compatible = "allwinner,suniv-f1c100s-ic";
  69. reg = <0x01c20400 0x400>;
  70. interrupt-controller;
  71. #interrupt-cells = <1>;
  72. };
  73. pio: pinctrl@1c20800 {
  74. compatible = "allwinner,suniv-f1c100s-pinctrl";
  75. reg = <0x01c20800 0x400>;
  76. interrupts = <38>, <39>, <40>;
  77. clocks = <&ccu 37>, <&osc24M>, <&osc32k>;
  78. clock-names = "apb", "hosc", "losc";
  79. gpio-controller;
  80. interrupt-controller;
  81. #interrupt-cells = <3>;
  82. #gpio-cells = <3>;
  83. uart0_pe_pins: uart0-pe-pins {
  84. pins = "PE0", "PE1";
  85. function = "uart0";
  86. };
  87. // modify by kashine
  88. mmc0_pins: mmc0-pins {
  89. pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
  90. function = "mmc0";
  91. };
  92. };
  93. timer@1c20c00 {
  94. compatible = "allwinner,suniv-f1c100s-timer";
  95. reg = <0x01c20c00 0x90>;
  96. interrupts = <13>;
  97. clocks = <&osc24M>;
  98. };
  99. wdt: watchdog@1c20ca0 {
  100. compatible = "allwinner,suniv-f1c100s-wdt",
  101. "allwinner,sun4i-a10-wdt";
  102. reg = <0x01c20ca0 0x20>;
  103. };
  104. uart0: serial@1c25000 {
  105. compatible = "snps,dw-apb-uart";
  106. reg = <0x01c25000 0x400>;
  107. interrupts = <1>;
  108. reg-shift = <2>;
  109. reg-io-width = <4>;
  110. clocks = <&ccu 38>;
  111. resets = <&ccu 24>;
  112. status = "disabled";
  113. };
  114. uart1: serial@1c25400 {
  115. compatible = "snps,dw-apb-uart";
  116. reg = <0x01c25400 0x400>;
  117. interrupts = <2>;
  118. reg-shift = <2>;
  119. reg-io-width = <4>;
  120. clocks = <&ccu 39>;
  121. resets = <&ccu 25>;
  122. status = "disabled";
  123. };
  124. uart2: serial@1c25800 {
  125. compatible = "snps,dw-apb-uart";
  126. reg = <0x01c25800 0x400>;
  127. interrupts = <3>;
  128. reg-shift = <2>;
  129. reg-io-width = <4>;
  130. clocks = <&ccu 40>;
  131. resets = <&ccu 26>;
  132. status = "disabled";
  133. };
  134. // modify by kashine
  135. mmc0: mmc@1c0f000 {
  136. compatible = "allwinner,suniv-f1c100s-mmc",
  137. "allwinner,sun7i-a20-mmc";
  138. reg = <0x01c0f000 0x1000>;
  139. clocks = <&ccu CLK_BUS_MMC0>,
  140. <&ccu CLK_MMC0>,
  141. <&ccu CLK_MMC0_OUTPUT>,
  142. <&ccu CLK_MMC0_SAMPLE>;
  143. clock-names = "ahb",
  144. "mmc",
  145. "output",
  146. "sample";
  147. resets = <&ccu RST_BUS_MMC0>;
  148. reset-names = "ahb";
  149. interrupts = <23>;
  150. pinctrl-names = "default";
  151. pinctrl-0 = <&mmc0_pins>;
  152. status = "disabled";
  153. #address-cells = <1>;
  154. #size-cells = <0>;
  155. };
  156. };
  157. };

 在.dts文件里面进行以下修改,首先使能mmc:

  1. &mmc0 {
  2. vmmc-supply = <&reg_vcc3v3>;
  3. bus-width = <4>;
  4. broken-cd;
  5. status = "okay";
  6. };

然后这个是墨云在根节点下添加的一个结点,他并没有说明为什么添加,我百度了一下,这是一个电源管理相关的结点,使用regulator-fixed来实现使用GPIO控制某个电源开关 ,希望在开机时尽快输出高低电平来控制电源。详见。Rockchip RK3588 kernel dts解析之regulator-fixed

  1. reg_vcc3v3: vcc3v3 {
  2. compatible = "regulator-fixed";
  3. regulator-name = "vcc3v3";
  4. regulator-min-microvolt = <3300000>;
  5. regulator-max-microvolt = <3300000>;
  6. };

.dts文件修改完成之后下所示:

  1. // SPDX-License-Identifier: (GPL-2.0+ OR X11)
  2. /*
  3. * Copyright 2018 Icenowy Zheng <icenowy@aosc.io>
  4. */
  5. /dts-v1/;
  6. #include "suniv-f1c100s.dtsi"
  7. / {
  8. model = "Lichee Pi Nano";
  9. compatible = "licheepi,licheepi-nano", "allwinner,suniv-f1c100s";
  10. aliases {
  11. serial0 = &uart0;
  12. };
  13. chosen {
  14. stdout-path = "serial0:115200n8";
  15. };
  16. // modify by kashine
  17. reg_vcc3v3: vcc3v3 {
  18. compatible = "regulator-fixed";
  19. regulator-name = "vcc3v3";
  20. regulator-min-microvolt = <3300000>;
  21. regulator-max-microvolt = <3300000>;
  22. };
  23. };
  24. &uart0 {
  25. pinctrl-names = "default";
  26. pinctrl-0 = <&uart0_pe_pins>;
  27. status = "okay";
  28. };
  29. // modify by kashine
  30. &mmc0 {
  31. vmmc-supply = <&reg_vcc3v3>;
  32. bus-width = <4>;
  33. broken-cd;
  34. status = "okay";
  35. };

7、设备树编译与下载

在Linux内核根目录下,使用以下命令重新编译设备树,内核无需重新编译。

make dtbs

在内核源码根目录使用make dtbs编译变成后生成设备树文件,如下图所示。

编译完成后,将新的设备树文件(.dtb)拷贝到TF卡的第二分区,zImage无需修改,打开串口调试助手,上电复位,成功启动根文件系统!!!,成功打印出“Welcome to kashine linux system.”,root为管理员用户名,密码为前面设置的“good luck!”,将路径切换到根目录之后,查看根目录的文件夹,如下图所示。

 在上电成功启动根文件系统之后,不要直接拔掉USB断电,可能造成根文件系统损坏,使用poweroff命令关闭根文件系统后断电。


三、参考内容

1. 墨云uboot移植

2. 正点原子《嵌入式Linux驱动开发指南》      


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/712752
推荐阅读
相关标签
  

闽ICP备14008679号