Basic operation of rootfs root file system creation and mounting in linux system

Today I will introduce to you how to create and mount the rootfs root file system . I hope this article will be helpful to you.

This chapter mainly explains in detail how to create and mount the rootfs root file system.

Two of the "Big Three" of Linux have been completed, and only the last rootfs (root file system) is left. In this chapter, we will learn the composition of the root file system and how to build a root file system. This is the last step of Linux transplantation. After the root file system is built, it means that we already have a complete, minimal system that can run.

In the future, we will write and test the Linux driver on this minimal system, transplant some third-party components, and gradually improve this minimal system. Finally, a relatively complete operating system with complete functions and complete drivers is obtained.

1. Introduction to root file system

The root file system is also generally called rootfs, so what is the root file system? When seeing the words "file system", many people, including me, first think of file systems such as FATFS, FAT, EXT4, YAFFS and NTFS. Here, the root file system is not a file system code such as FATFS. The file system code such as EXT4 is part of the Linux kernel. The root file system in Linux is more like a folder or directory (in my opinion, it is just a folder, just a special folder). There will be many subdirectories in this directory.

There will be many files in the root directory and subdirectories, which are necessary for Linux to run, such as libraries, commonly used software and commands, device files, configuration files, etc. When we talk about file systems from now on, unless otherwise specified, we will refer to the root file system. For a professional explanation of the root file system, Baidu Encyclopedia says this.

The root file system is the first file system mounted when the kernel starts. The kernel code image file is stored in the root file system, and the system boot program will store some basic files from the root file system after it is mounted. Initialization scripts and services are loaded into memory to run.

Baidu Encyclopedia says that the kernel code image file is stored in the root file system, but our embedded Linux does not save the kernel code image in the root file system, but saves it elsewhere. For example, the designated storage address of NAND Flash and the EMMC dedicated partition. The root file system is the first file system mounted after the Linux kernel is started, and then initialization scripts, such as rcS, inittab, etc., are read from the root file system. The root file system and the Linux kernel are separate. The Linux kernel alone cannot work properly and must be paired with the root file system. If the root file system is not provided, the Linux kernel will prompt a kernel panic when booting.

The word "root" in the root file system illustrates the importance of this file system. It is the root of other file systems. Without this "root", other file systems or software cannot work. For example, our commonly used commands such as ls, mv, ifconfig and so on are actually small softwares, but these softwares do not have graphical interfaces and need to enter commands to run. These small software are stored in the root file system. Where did these small software come from? This is the purpose of our tutorial in this chapter, to teach you to build your own root file system. This root file system is the minimum root file system that meets the requirements of Linux operation. We can continue to fill this minimum root file system according to our actual work needs in the future. , eventually making it a relatively complete root file system.

The selection and configuration of the root file system has a significant impact on the performance, functionality, and reliability of the operating system. Different application scenarios and requirements may require a customized root file system to meet specific requirements. Therefore, the design and management of the root file system is one of the important aspects of operating system development and system management.

2. Root file system building software

There are three commonly used software for building root file systems, namely busybox, buildroot, and yocto. busybox only helps us build some commonly used commands and files. Some files in the lib library and /etc directory need to be created manually by ourselves. Moreover, the root file system built by busybox does not have user name and password settings by default. In subsequent experiments, we will also need to transplant some third-party software and libraries ourselves, such as alsa, iperf, mplayer, etc. So is there a fool-like method or software that not only includes the functions of busybox, but also integrates various software. We can choose whatever software we need without us having to transplant it.

The answer is definitely yes. buildroot is such a tool. buildroot is a step up from busybox. buildroot not only integrates busybox, but also integrates various common third-party libraries and software. You can choose what you need, just follow It's like going to a buffet and you can get whatever you want. buildroot greatly facilitates our embedded Linux developers to build a practical root file system.

Building the root file system step by step starting from busybox is suitable for learning and understanding the composition of the root file system, but it is not suitable for making products (mainly because if you build it yourself, there will be many imperfections and unnoticed details). buildroot will help us handle various details, and the root file system will be more reasonable and effective. Therefore, when making products, it is recommended that you use buildroot to build your own root file system. Of course, there are many software similar to buildroot, such as yocto, which will be discussed later.

Buildroot is very similar to uboot and Linux Kernel. We need to download the source code from its official website and then configure it, such as setting up a cross-compiler, setting target CPU parameters, etc. The most important thing is to select the required third-party library or software. After everything is configured, you can compile it. After the compilation is completed, the compilation results will be stored in a folder, which is the root file system.

3. busybox builds root file system

3.1 Introduction to BusyBox

Its name is divided into "Busy" and "Box", which means busy box. The box is used to store things, and it is busy because it provides the files required by the root file system. BusyBox is a software that integrates a large number of Linux commands and tools, such as ls, mv, ifconfig and other commands. BusyBox will provide them. BusyBox is a large toolbox that integrates many Linux tools and commands. Generally, download the source code of BusyBox, then configure BusyBox, select the functions you want, and finally compile.

BusyBox official website download address is:

https://busybox.net/

Log in to the official website and we can see that the interface is very simple.

picture

There is a line "Download Source" in the "Get BusyBox" column on the left side of the official website. Click "Download Source" to open the download page of BusyBox, as shown below:

picture

As can be seen from the above, the latest BusyBox version is 1.36.1. However, we will not consider using the latest version for building this time. Just choose a newer one. This time we chose busybox-  1.32.0.tar.bz2 . After BusyBox is downloaded, we start building the root file system.

3.2 Compile BusyBox and build the root file system

Busybox compilation and build process:

  1. Use make menuconfig to build a graphical configuration interface.

  2. Configure the installation path, compilation tools, command function enablement, etc. of busybox by configuring the options on the graphical configuration interface. Generate .config configuration file

  3. Use make to compile busybox

  4. Use the make install command to install the root file system generated by busybox

  5. Improve root file system

  6. Using and testing the root file system

1. Create a folder to save the root file system

mkdir rootfs

2. Unzip the BusyBox compressed package to the rootfs directory

tar -jxvf busybox-1.32.0.tar.bz2

3. Modify Makefile and add compiler

# Cross compiling and selecting different set of gcc/bin-utils
# ---------------------------------------------------------------------------
#
# When performing cross compilation for other architectures ARCH shall be set
# to the target architecture. (See arch/* for the possibilities).
# ARCH can be set during invocation of make:
# make ARCH=ia64
# Another way is to have ARCH set in the environment.
# The default ARCH is the host where make is executed.

# CROSS_COMPILE specify the prefix used for all executables used
# during compilation. Only gcc and related bin-utils executables
# are prefixed with $(CROSS_COMPILE).
# CROSS_COMPILE can be set on the command line
# make CROSS_COMPILE=ia64-linux-
# Alternatively CROSS_COMPILE can be set in the environment.
# Default value for CROSS_COMPILE is not to prefix executables
# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile

CROSS_COMPILE ?= arm-linux-gnueabihf-
# bbox: we may have CONFIG_CROSS_COMPILER_PREFIX in .config,
# and it has not been included yet... thus using an awkward syntax.
ifeq ($(CROSS_COMPILE),)
CROSS_COMPILE := $(shell grep ^CONFIG_CROSS_COMPILER_PREFIX .config 2>/dev/null)
CROSS_COMPILE := $(subst CONFIG_CROSS_COMPILER_PREFIX=,,$(CROSS_COMPILE))
CROSS_COMPILE := $(subst ",,$(CROSS_COMPILE))
#")
endif

# SUBARCH tells the usermode build what the underlying arch is.  That is set
# first, and if a usermode build is happening, the "ARCH=um" on the command
# line overrides the setting of ARCH below.  If a native build is happening,
# then ARCH is assigned, getting whatever value it gets normally, and
# SUBARCH is subsequently ignored.

ifneq ($(CROSS_COMPILE),)
SUBARCH := $(shell echo $(CROSS_COMPILE) | cut -d- -f1 | sed 's:^.*/::g')
else
SUBARCH := $(shell uname -m)
endif
SUBARCH := $(shell echo $(SUBARCH) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
                     -e s/arm.*/arm/ -e s/sa110/arm/ \
                     -e s/s390x/s390/ -e s/parisc64/parisc/ \
                     -e s/ppc.*/powerpc/ -e s/mips.*/mips/ )

ARCH ?= arm

Modify the contents of the two lines "CROSS_COMPILE ?=" and "ARCH ?= arm" and modify the above code.

3.3 busybox Chinese character support

If you compile directly with the default configuration of busybox, Chinese characters will not be displayed properly when using SecureCRT, and Chinese characters will be displayed as "?". For example, your Chinese directory and Chinese files will be displayed as "?". This is because the shell command in busybox restricts the display of Chinese input, so we need to modify the source code of busybox to cancel the restriction of busybox on Chinese display. Let's modify busybox step by step to support Chinese.

3.3.1 Modify printable_string2 function

Open the file busybox-1.32.0/libbb/printable_string.c, find the function  printable_string2 , the specific content of the function is as follows:

const char* FAST_FUNC printable_string2(uni_stat_t *stats, const char *str)
{
    char *dst;
    const char *s;

    s = str;
    while (1) {
        unsigned char c = *s;
        if (c == '\0') {
            /* 99+% of inputs do not need conversion */
            if (stats) {
                stats->byte_count = (s - str);
                stats->unicode_count = (s - str);
                stats->unicode_width = (s - str);
            }
            return str;
        }
        if (c < ' ')
            break;
        if (c >= 0x7f)
            break;
        s++;
    }

#if ENABLE_UNICODE_SUPPORT
    dst = unicode_conv_to_printable(stats, str);
#else
    {
        char *d = dst = xstrdup(str);
        while (1) {
            unsigned char c = *d;
            if (c == '\0')
                break;
            if (c < ' ' || c >= 0x7f)
                *d = '?';
            d++;
        }
        if (stats) {
            stats->byte_count = (d - dst);
            stats->unicode_count = (d - dst);
            stats->unicode_width = (d - dst);
        }
    }
#endif
    return auto_string(dst);
}

It can be seen from lines 20 and 21 and lines 34 and 35 of the above code: characters greater than 0x7F are directly broken, or directly replaced by "?" Therefore, even if the Linux kernel is set to support Chinese, it cannot be displayed and is replaced by "?". The modified code is as follows:

const char* FAST_FUNC printable_string2(uni_stat_t *stats, const char *str)
{
    char *dst;
    const char *s;

    s = str;
    while (1) {
        unsigned char c = *s;
        if (c == '\0') {
            /* 99+% of inputs do not need conversion */
            if (stats) {
                stats->byte_count = (s - str);
                stats->unicode_count = (s - str);
                stats->unicode_width = (s - str);
            }
            return str;
        }
        if (c < ' ')
            break;
        /* 注释掉下面这个两行代码 */
/*
        if (c >= 0x7f)
            break;
*/
        s++;
    }

#if ENABLE_UNICODE_SUPPORT
    dst = unicode_conv_to_printable(stats, str);
#else
    {
        char *d = dst = xstrdup(str);
        while (1) {
            unsigned char c = *d;
            if (c == '\0')
                break;
            /* 修改下面代码 */    
            /* if (c < ' ' || c >= 0x7f) */
            if( c < ' ')
                *d = '?';
            d++;
        }
        if (stats) {
            stats->byte_count = (d - dst);
            stats->unicode_count = (d - dst);
            stats->unicode_width = (d - dst);
        }
    }
#endif
    return auto_string(dst);
}

3.3.2 Modify the unicode_conv_to_printable2 function

Open the file busybox-1.29.0/libbb/unicode.c, find the function  nicode_conv_to_printable2 , the specific content of the function is as follows:

static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
{
    char *dst;
    unsigned dst_len;
    unsigned uni_count;
    unsigned uni_width;

    if (unicode_status != UNICODE_ON) {
        char *d;
        if (flags & UNI_FLAG_PAD) {
            d = dst = xmalloc(width + 1);
            while ((int)--width >= 0) {
                unsigned char c = *src;
                if (c == '\0') {
                    do
                        *d++ = ' ';
                    while ((int)--width >= 0);
                    break;
                }
                *d++ = (c >= ' ' && c < 0x7f) ? c : '?';
                src++;
            }
            *d = '\0';
        } else {
            d = dst = xstrndup(src, width);
            while (*d) {
                unsigned char c = *d;
                if (c < ' ' || c >= 0x7f)
                    *d = '?';
                d++;
            }
        }
        if (stats) {
            stats->byte_count = (d - dst);
            stats->unicode_count = (d - dst);
            stats->unicode_width = (d - dst);
        }
        return dst;
    }
    ...

It can be seen from the code that on line 20, when the character is less than a space or the character is greater than 0X7F, *d++ is '?'. Lines 28 and 29 are the same. When the character is less than a space or the character is greater than 0X7F, *d is also '?'. The modified code is as follows:

static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
{
    char *dst;
    unsigned dst_len;
    unsigned uni_count;
    unsigned uni_width;

    if (unicode_status != UNICODE_ON) {
        char *d;
        if (flags & UNI_FLAG_PAD) {
            d = dst = xmalloc(width + 1);
            while ((int)--width >= 0) {
                unsigned char c = *src;
                if (c == '\0') {
                    do
                        *d++ = ' ';
                    while ((int)--width >= 0);
                    break;
                }
                /* 修改下面一行代码 */
                /* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
                *d++ = (c >= ' ') ? c : '?';
                src++;
            }
            *d = '\0';
        } else {
            d = dst = xstrndup(src, width);
            while (*d) {
                unsigned char c = *d;
                /* 修改下面一行代码 */
                /* if (c < ' ' || c >= 0x7f) */
                if (c < ' ')
                    *d = '?';
                d++;
            }
        }
        if (stats) {
            stats->byte_count = (d - dst);
            stats->unicode_count = (d - dst);
            stats->unicode_width = (d - dst);
        }
        return dst;
    }
    ...

The Chinese character support in busybox is related to code modification, but in the end, busybox needs to be configured to enable unicode code so that Chinese characters can be displayed.

3.4 configure busybox

The steps for compiling busybox are the same as compiling uboot and Linux kernel. First, make the default configuration and then compile. Just choose the default configuration to configure busybox:

make defconfig

busybox also supports graphical configuration. Enter the graphical configuration interface to perform the following configuration:

picture

The configuration path is as follows:

Location:
    -> Settings
        -> Build static binary (no shared libs)

The option " Build static binary (no shared libs) " is used to decide whether to compile busybox statically or dynamically. If compiled statically, no library files are needed, but the compiled library will be very large. Dynamic compilation requires library files in the root file system, but the compiled busybox will be much smaller. We can't use static compilation here! Because if you use static compilation, DNS will have problems! Domain name resolution cannot be performed. The configuration is as follows:

picture

Continue to configure the following path configuration items:

Location:
    -> Settings
        -> vi-style line editing commands

As shown below:

picture

Continue to configure the following path configuration items:

Location:
    -> Linux Module Utilities
        -> Simplified modutils

" Simplified modutils " is selected by default , here we need to uncheck it! ! As shown below:

picture

Continue to configure the following path configuration items:

Location:
    -> Linux System Utilities
        -> mdev (17 kb) //确保下面的全部选中,默认都是选中的

As shown below:

picture

The last is to enable the unicode encoding of busybox to support Chinese, the configuration path is as follows:

Location:
    -> Settings
        -> Support Unicode //选中
            -> Check $LC_ALL, $LC_CTYPE and $LANG environment variables //选中

As shown below:

picture

At this point, the default configuration of busybox is complete. You can also choose to configure other options according to your actual needs. However, for beginners, I do not recommend making other modifications, as compilation errors may occur. The next step is to compile busybox.

3.5 Compile busybox

The first is to specify the storage directory of the compilation results. Currently, the compilation results are stored in the previously created rootfs directory. Enter the following command:

make 
make install CONFIG_PREFIX=/home/toto/workspace/rootfs/rootfs

COFIG_PREFIX specifies the storage directory of the compilation results. For example, I store it in the "/home/toto/workspace/rootfs/rootfs" directory and wait for the compilation to complete. After compilation is completed, the following figure appears:

 /home/toto/workspace/rootfs/rootfs//usr/sbin/setlogcons -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/svlogd -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/telnetd -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/tftpd -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/ubiattach -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/ubidetach -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/ubimkvol -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/ubirename -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/ubirmvol -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/ubirsvol -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/ubiupdatevol -> ../../bin/busybox
  /home/toto/workspace/rootfs/rootfs//usr/sbin/udhcpd -> ../../bin/busybox


--------------------------------------------------
You will probably need to make your busybox binary
setuid root to ensure all configured applets will
work properly.
--------------------------------------------------

After the compilation is complete, all the tools and files in busybox will be installed in the rootfs directory. The contents of the rootfs directory are as follows:

toto@toto:~/workspace/rootfs/rootfs$ ls
bin  linuxrc  sbin  usr

As can be seen from the above, there are three directories bin, sbin and usr under the rootfs directory, as well as the file linuxrc. The Linux kernel init process will eventually search for the user space init program. Once found, it will run the user space init program to switch to user mode. If bootargs sets init=/linuxrc, then linuxrc can be used as the init program in user space, so the init program in user space is generated by busybox.

The work of busybox is completed, but the root file system cannot be used at this time, and some other files are needed. Let's continue to improve rootfs.

4. Add lib library to the root file system

4.1 Add library files to the "/lib" directory of the rootfs file system

Applications in Linux generally require dynamic libraries. Of course, you can also compile them into static ones, but the static executable file will be very large. If you compile it dynamically, you need a dynamic library, so we need to add the dynamic library to the root file system first.

Create a folder named "lib" in rootfs with the following command:

mkdir lib

The lib folder is created, where do the library files come from? The lib library file is obtained from the cross-compiler. When we built the cross-compilation environment earlier, we stored the cross-compiler in the "/usr/local/arm/" directory.

There are many library files in the cross-compiler. We definitely don't know what these library files do specifically, so we don't care about them. We put all the library files in our root file system. The root file system created in this way will definitely be very large, but it is still in the learning stage, so just focus on how to successfully transplant rootfs.

Enter the directory corresponding to the following path:

/home/toto/workspace/tools/gcc-linaro-7.5.0-2019.12-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/
toto@toto:~/workspace/rootfs/rootfs$ ls /home/toto/workspace/tools/gcc-linaro-7.5.0-2019.12-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/
debug                    libcidn.so.1          libgomp.so             libnss_dns.so.2         libssp_nonshared.a
ld-2.25.so               libcilkrts.a          libgomp.so.1           libnss_files-2.25.so    libssp.so
ld-linux-armhf.so.3      libcilkrts.so         libgomp.so.1.0.0       libnss_files.so.2       libssp.so.0
ldscripts                libcilkrts.so.5       libgomp.spec           libnss_hesiod-2.25.so   libssp.so.0.0.0
libanl-2.25.so           libcilkrts.so.5.0.0   libitm.a               libnss_hesiod.so.2      libstdc++.a
libanl.so.1              libcilkrts.spec       libitm.so              libnss_nis-2.25.so      libstdc++fs.a
libasan.a                libcrypt-2.25.so      libitm.so.1            libnss_nisplus-2.25.so  libstdc++.so
libasan_preinit.o        libcrypt.so.1         libitm.so.1.0.0        libnss_nisplus.so.2     libstdc++.so.6
libasan.so               libc.so.6             libitm.spec            libnss_nis.so.2         libstdc++.so.6.0.24
libasan.so.4             libdl-2.25.so         libm-2.25.so           libpcprofile.so         libstdc++.so.6.0.24-gdb.py
libasan.so.4.0.0         libdl.so.2            libmemusage.so         libpthread-2.25.so      libsupc++.a
libatomic.a              libgcc_s.so           libm.so.6              libpthread.so.0         libthread_db-1.0.so
libatomic.so             libgcc_s.so.1         libnsl-2.25.so         libresolv-2.25.so       libthread_db.so.1
libatomic.so.1           libgfortran.a         libnsl.so.1            libresolv.so.2          libubsan.a
libatomic.so.1.2.0       libgfortran.so        libnss_compat-2.25.so  librt-2.25.so           libubsan.so
libBrokenLocale-2.25.so  libgfortran.so.4      libnss_compat.so.2     librt.so.1              libubsan.so.0
libBrokenLocale.so.1     libgfortran.so.4.0.0  libnss_db-2.25.so      libsanitizer.spec       libubsan.so.0.0.0
libc-2.25.so             libgfortran.spec      libnss_db.so.2         libSegFault.so          libutil-2.25.so
libcidn-2.25.so          libgomp.a             libnss_dns-2.25.so     libssp.a                libutil.so.1

As you can see from the above, there are many .so and .a files. These are library files. Copy all .so and .a files in this directory to the rootfs/lib directory. The copy command is as follows:

cp *so* *.a /home/toto/workspace/rootfs/rootfs/lib/ -d

-d: means copying a symbolic link. There is a special library file here: ld-linux-armhf.so.3. This library file is also a symbolic link, which is equivalent to a shortcut under Windows. It will be linked to the library ld-2.25.so. Enter the command "ls ld-linux-armhf.so.3 -l" to view the details of this file, as shown below:

toto@toto:~/workspace/rootfs/rootfs/lib$ ls ld-linux-armhf.so.3  -l
lrwxrwxrwx 1 toto toto 10 6月  12 16:02 ld-linux-armhf.so.3 -> ld-2.25.so

It can be seen that there is a "->" after ld-linux-armhf.so.3, indicating that it is a soft link file, linked to the file ld-2.25.so, because it is a "shortcut", so the size is only 10B . However, ld-linuxarmhf.so.3 cannot be used as a symbolic link, otherwise the program will not be executed in the root file system! So we need ldlinux-armhf.so.3 to complete the counterattack and change from "shortcut" to "original". The method is very simple, that is, copy ld-linuxarmhf.so.3 again, but do not copy the soft link. First Delete the ld-linux-armhf.so.3 file in rootfs/lib with the following command:

rm ld-linux-armhf.so.3

Then re-enter the /home/toto/workspace/tools/gcc-linaro-7.5.0-2019.12-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/ directory and copy ld-linux-armhf again. so.3, the command is as follows:

cp ld-linux-armhf.so.3 /home/toto/workspace/rootfs/rootfs/lib/

After the copy is completed, go to the rootfs/lib directory to view the details of the ld-linux-armhf.so.3 file, as shown below:

toto@toto:~/workspace/rootfs/rootfs/lib$ ls ld-linux-armhf.so.3 -l
-rwxr-xr-x 1 toto toto 1110968 6月  12 16:17 ld-linux-armhf.so.3

It can be seen that ld-linux-armhf.so.3 is no longer a soft connection at this time, but a real library file, and the file size is 1110968B. Continue into the following directory:

/home/toto/workspace/tools/gcc-linaro-7.5.0-2019.12-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/lib
toto@toto:~/workspace/tools/gcc-linaro-7.5.0-2019.12-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/lib$ ls
debug              libatomic.so.1       libgfortran.a         libgomp.spec        libssp.so                   libsupc++.a
ldscripts          libatomic.so.1.2.0   libgfortran.so        libitm.a            libssp.so.0                 libubsan.a
libasan.a          libcilkrts.a         libgfortran.so.4      libitm.so           libssp.so.0.0.0             libubsan.so
libasan_preinit.o  libcilkrts.so        libgfortran.so.4.0.0  libitm.so.1         libstdc++.a                 libubsan.so.0
libasan.so         libcilkrts.so.5      libgfortran.spec      libitm.so.1.0.0     libstdc++fs.a               libubsan.so.0.0.0
libasan.so.4       libcilkrts.so.5.0.0  libgomp.a             libitm.spec         libstdc++.so
libasan.so.4.0.0   libcilkrts.spec      libgomp.so            libsanitizer.spec   libstdc++.so.6
libatomic.a        libgcc_s.so          libgomp.so.1          libssp.a            libstdc++.so.6.0.24
libatomic.so       libgcc_s.so.1        libgomp.so.1.0.0      libssp_nonshared.a  libstdc++.so.6.0.24-gdb.py

There are also many .so and .a library files in this directory, we will also copy them to the rootfs/lib directory, the command is as follows:

cp *so* *.a /home/toto/workspace/rootfs/rootfs/lib/ -d

These are the library files in the rootfs/lib directory. After completion, the rootfs/lib directory is as follows:

toto@toto:~/workspace/rootfs/rootfs/lib$ ls
ld-2.25.so               libcidn-2.25.so      libgfortran.so.4      libnss_compat-2.25.so   libpthread-2.25.so  libstdc++.so.6
ld-linux-armhf.so.3      libcidn.so.1         libgfortran.so.4.0.0  libnss_compat.so.2      libpthread.so.0     libstdc++.so.6.0.24
libanl-2.25.so           libcilkrts.a         libgomp.a             libnss_db-2.25.so       libresolv-2.25.so   libstdc++.so.6.0.24-gdb.py
libanl.so.1              libcilkrts.so        libgomp.so            libnss_db.so.2          libresolv.so.2      libsupc++.a
libasan.a                libcilkrts.so.5      libgomp.so.1          libnss_dns-2.25.so      librt-2.25.so       libthread_db-1.0.so
libasan.so               libcilkrts.so.5.0.0  libgomp.so.1.0.0      libnss_dns.so.2         librt.so.1          libthread_db.so.1
libasan.so.4             libcrypt-2.25.so     libitm.a              libnss_files-2.25.so    libSegFault.so      libubsan.a
libasan.so.4.0.0         libcrypt.so.1        libitm.so             libnss_files.so.2       libssp.a            libubsan.so
libatomic.a              libc.so.6            libitm.so.1           libnss_hesiod-2.25.so   libssp_nonshared.a  libubsan.so.0
libatomic.so             libdl-2.25.so        libitm.so.1.0.0       libnss_hesiod.so.2      libssp.so           libubsan.so.0.0.0
libatomic.so.1           libdl.so.2           libm-2.25.so          libnss_nis-2.25.so      libssp.so.0         libutil-2.25.so
libatomic.so.1.2.0       libgcc_s.so          libmemusage.so        libnss_nisplus-2.25.so  libssp.so.0.0.0     libutil.so.1
libBrokenLocale-2.25.so  libgcc_s.so.1        libm.so.6             libnss_nisplus.so.2     libstdc++.a
libBrokenLocale.so.1     libgfortran.a        libnsl-2.25.so        libnss_nis.so.2         libstdc++fs.a
libc-2.25.so             libgfortran.so       libnsl.so.1           libpcprofile.so         libstdc++.so

4.2 Add library files to the "usr/lib" directory of the rootfs file system

Create a directory named lib in the usr directory of rootfs, and copy the library files in the following directory to the rootfs/usr/lib directory:

/home/toto/workspace/tools/gcc-linaro-7.5.0-2019.12-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
toto@toto:~/workspace/tools/gcc-linaro-7.5.0-2019.12-i686_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib$ ls
audit    libanl.a            libc_nonshared.a  libg.a       libnsl.so         libnss_nisplus.so       libresolv.so     libutil.so
crt1.o   libanl.so           libcrypt.a        libieee.a    libnss_compat.so  libnss_nis.so           librpcsvc.a      Mcrt1.o
crti.o   libBrokenLocale.a   libcrypt.so       libm.a       libnss_db.so      libpthread.a            librt.a          Scrt1.o
crtn.o   libBrokenLocale.so  libc.so           libmcheck.a  libnss_dns.so     libpthread_nonshared.a  librt.so
gconv    libc.a              libdl.a           libm.so      libnss_files.so   libpthread.so           libthread_db.so
gcrt1.o  libcidn.so          libdl.so          libnsl.a     libnss_hesiod.so  libresolv.a             libutil.a

Copy both the .so and .a library files in this directory to the rootfs/usr/lib directory. The command is as follows:

cp *so* *.a /home/toto/workspace/rootfs/rootfs/usr/lib/ -d

The completed rootfs/usr/lib directory looks as follows:

toto@toto:~/workspace/rootfs/rootfs/usr/lib$ ls 
libanl.a            libcidn.so        libdl.a    libmcheck.a       libnss_db.so       libnss_nis.so           libresolv.so     libutil.a
libanl.so           libc_nonshared.a  libdl.so   libm.so           libnss_dns.so      libpthread.a            librpcsvc.a      libutil.so
libBrokenLocale.a   libcrypt.a        libg.a     libnsl.a          libnss_files.so    libpthread_nonshared.a  librt.a
libBrokenLocale.so  libcrypt.so       libieee.a  libnsl.so         libnss_hesiod.so   libpthread.so           librt.so
libc.a              libc.so           libm.a     libnss_compat.so  libnss_nisplus.so  libresolv.a             libthread_db.so

At this point, all the library files of the root file system have been added. You can use the "du" command to check the sizes of the rootfs/lib and rootfs/usr/lib directories. The command is as follows:

toto@toto:~/workspace/rootfs/rootfs$ du ./lib ./usr/lib -sh 
114M    ./lib
45M     ./usr/lib

It can be seen that the sizes of lib and usr/lib are 114MB and 45MB respectively, and the sum is 114+45=159MB. Very big! There is no way, because we have copied all the library files, whether we need them or not. In the real work later, the library files that you need are generally copied, so that the root files produced are much smaller.

4.3 Create other required folders

Create other folders in the root file system, such as dev, proc, mnt, sys, tmp and root, etc. After the creation is completed, it will look like this:

toto@toto:~/workspace/rootfs/rootfs$ ls
bin  dev  lib  linuxrc  mnt  proc  root  sbin  sys  tmp  usr

At this point, the root file system is almost ready, but whether it can be successfully mounted and used, let’s power on and test it to find out!

5. Root file system mounting method

There are three ways to mount the root file system:

  • NFS mount rootfs

  • SD card mount rootfs

  • eMMC mount rootfs

In order to simulate actual work, before starting to transplant the Linux system to the development board, all the partition information and all system information in the eMMC were erased through the mmc command under u-boot. Therefore, this time we can only burn the system to the SD card, then boot the system from the SD card, and mount the root file system in the SD card.

The following will introduce three methods of mounting the root file system.

5.1 NFS mount rootfs

5.1.1 Install NFS service

Use the following command to install the NFS service:

sudo apt-get install nfs-kernel-server rpcbind

5.1.2 Configure network access file directory

Wait for the installation to be completed. The created nfs folder will be used by the nfs server. In the future, we can access the nfs folder through the network file system on the development board. To configure nfs first, use the following command to open the nfs configuration file /etc/exports:

sudo vi /etc/exports

After opening /etc/exports, add the following content:

/home/toto/workspace/nfs *(rw,sync,no_root_squash)
/home/toto/workspace/rootfs/rootfs *(rw,sync,no_root_squash)

After the addition is complete, /etc/exports looks like this:

# /etc/exports: the access control list for filesystems which may be exported
#               to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/home/toto/workspace/nfs *(rw,sync,no_root_squash)
/home/toto/workspace/rootfs/rootfs *(rw,sync,no_root_squash)

5.1.3 Enable NFS service

To restart the NFS service, use the following command:

sudo /etc/init.d/nfs-kernel-server restart

5.1.4 NFS mount rootfs

To mount rootfs using NFS, the bootargs environment variable in uboot will set the value of "root", so we can change the value of root to NFS mounting.

There is a corresponding document in the Linux kernel source code explaining how to set it up, the document is Documentation/filesystems/nfs/nfsroot.txt, the format is as follows:

root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>

The meaning of each parameter is as follows:

  • [server-ip]: Server IP address, which is the IP address of the host where the root file system is stored. That is the IP address of Ubuntu. For example, my Ubuntu host IP address is 192.168.1.250

  • [root-dir]: The storage path of the root file system, for example, mine is /home/toto/workspace/rootfs/rootfs/

  • [nfs-options]: Other optional options for NFS, generally not set

  • [client-ip]: Client IP address, that is, the IP address of our development board. After the Linux kernel starts, this IP address will be used to configure the development board. This address must be in the same network segment as the Ubuntu host and is not used by other devices. Use the ping command in Ubuntu to ping to know whether the IP address to be set is in use. If it cannot be pinged, it means that it is not in use. Use, then it can be set to the IP address of the development board, for example, I can set it to 192.168.1.251

  • [server-ip]: server IP address, as mentioned earlier

  • [gw-ip]: Gateway address, mine is 192.168.1.1

  • [netmask]: subnet mask, mine is 255.255.255.0

  • [hostname]: The name of the client, generally not set, this value can be empty

  • [device]: Device name, that is, the name of the network card, usually eth0, eth1..., the ENET2 of the I.MX6U-ALPHA development board of Punctual Atomic is eth0, and the ENET1 is eth1. If your computer has only one network card, then basically it can only be eth0. Here we use ENET2, so the network card name is eth0

  • [autoconf]: Automatic configuration, generally not used, so set to off

  • [dns0-ip]: DNS0 server IP address, not used

  • [dns1-ip]: DNS1 server IP address, not used

According to the above format, the root value of the bootargs environment variable is as follows:

root=/dev/nfs nfsroot=192.168.1.43:/home/toto/workspace/rootfs/rootfs,proto=tcp rwip=192.168.1.100:192.168.1.43:192.168.1.1:255.255.255.0::eth0:off

"proto=tcp" means using the TCP protocol, and "rw" means that the root file system mounted by nfs is readable and writable.

Start the development board, enter uboot command line mode, and then reset the bootargs environment variable. The command is as follows:

setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.250:/home/toto/workspace/rootfs/rootfs,proto=tcp rw ip=192.168.1.100:192.168.1.43:192.168.1.1:255.255.255.0::eth0:off' //设置 bootargs
saveenv //保存环境变量

After setting up, use the "boot" command to start the Linux kernel and test whether the root file system is mounted successfully.

5.2 SD card mount rootfs

Here, in order to save time, we directly use the script file ( imx6mksdboot.sh ) that solidifies the system of Ondian Atomic to the SD card , and just make some modifications on it. Let’s first take a look at what files are there:

boot  filesystem  imx6mksdboot.sh

in:

  • The boot folder is used to store the system's u-boot image file, linux kernel system, and device tree files (u-boot-imx6ull-14x14-ddr512-emmc.imx, zImage, imx6ull-14x14-emmc-10.1-1280x800-c .dtb);

  • The filesystem folder is used to store the packaged root file system compressed file (rootfs.tar.bz2);

  • imx6mksdboot.sh is a script file that burns the system to the SD card.

    First of all, let's understand what the script imx6mksdboot.sh roughly does. The script part is as follows:

#! /bin/sh
...
if [ -z $number ];then
  echo "使用默认参数:EMMC版本,DDR大小为512MB"
else
  case $number in
    1)  echo '您已经选择开发板参数为:EMMC版本,DDR大小为512MB'
    Uboot='u-boot-imx6ull-14x14-ddr512-emmc.imx'
    ;;
    2)  echo '您已经选择开发板参数为:EMMC版本,DDR大小为256MB'
    Uboot='u-boot-imx6ull-14x14-ddr256-emmc.imx'
    ;;
    3)  echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为512MB'
    Uboot='u-boot-imx6ull-14x14-ddr512-nand-sd.imx'
    ;;
    4)  echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为256MB'
    Uboot='u-boot-imx6ull-14x14-ddr256-nand-sd.imx'
    ;;
    *)  echo '输入的参数有误,退出制卡';exit;
    ;;
esac
fi
else
#命令行处理,根据选项获得参数
while [ $# -gt 0 ]; do
  case $1 in
    --help | -h)
      usage $0
      ;;
    -device) shift; device=$1; shift; ;;
    -flash) shift; flash=$1; shift; ;;
    -ddrsize) shift; ddrsize=$1; shift; ;;
    --version) version $0;;
    *) copy="$copy $1"; shift; ;;
  esac
done
  if [ $flash = "emmc" -a $ddrsize = "512" ];then
    Uboot='u-boot-imx6ull-14x14-ddr512-emmc.imx'
    echo '您已经选择开发板参数为:EMMC版本,DDR大小为512MB'

  elif [ $flash = "emmc" -a $ddrsize = "256" ];then
    Uboot='u-boot-imx6ull-14x14-ddr256-emmc.imx'
    echo '您已经选择开发板参数为:EMMC版本,DDR大小为256MB'

  elif [ $flash = "nand" -a $ddrsize = "512" ];then
    Uboot='u-boot-imx6ull-14x14-ddr512-nand-sd.imx'
    echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为512MB'

  elif [ $flash = "nand" -a $ddrsize = "256" ];then
    Uboot='u-boot-imx6ull-14x14-ddr256-nand-sd.imx'
    echo '您的开发板参数为:NAND FLASH版本,DDR大小为256MB'
  else
    echo '参数有误!'
    usage $0
  fi 
fi

#测试制卡包当前目录下是否缺失制卡所需要的文件
sdkdir=$PWD

if [ ! -d $sdkdir ]; then
   echo "错误: $sdkdir目录不存在"
   exit 1
fi

if [ ! -f $sdkdir/filesystem/ *.tar.* ]; then
  echo "错误: $sdkdir/filesystem/下找不到文件系统压缩包"
  exit 1
fi

if [ ! -f $sdkdir/boot/zImage ]; then
  echo "错误: $sdkdir/boot/下找不到zImage"
  exit 1
fi
#判断选择的块设备是否存在及是否是一个块设备
if [ ! -b $device ]; then
  echo "错误: $device 不是一个块设备文件"
  exit 1
fi
#这里防止选错设备,否则会影响Ubuntu系统的启动
if [ $device = '/dev/sda' ];then
  echo "请不要选择sda设备,/dev/sda通常是您的Ubuntu硬盘!
继续操作你的系统将会受到影响!脚本已自动退出"
  exit 1 
fi
echo "即将进行制作SD系统启动卡,大约花费几分钟时间,请耐心等待!"
echo "************************************************************"
echo "*         注意:这将会清除$device所有的数据               *"
echo "*         在脚本执行时请不要将$device拔出                 *"
echo "*             请按<Enter>确认继续                          *"
echo "************************************************************"
read enter

#格式化前要卸载
for i in `ls -1 $device?`; do
 echo "卸载 device '$i'"
 umount $i 2>/dev/null
done

#执行格式化$device
execute "dd if=/dev/zero of=$device bs=1024 count=1024"

#第一个分区为64M用来存放设备树与内核镜像文件,因为设备树与内核都比较小,不需要太大的空间
#第二个分区为SD卡的总大小-64M,用来存放文件系统
...
#两个分区处理
PARTITION1=${device}1
if [ ! -b ${PARTITION1} ]; then
        PARTITION1=${device}1
fi

PARTITION2=${device}2
if [ ! -b ${PARTITION2} ]; then
        PARTITION2=${device}2
fi

#第一个分区创建为Fat32格式
echo "格式化 ${device}1 ..."
if [ -b ${PARTITION1} ]; then
    mkfs.vfat -F 32 -n "boot" ${PARTITION1}
else
    echo "错误: /dev下找不到 SD卡 boot分区"
fi
#第二个分区创建为ext4格式
echo "格式化${device}2 ..."
if [ -b ${PARITION2} ]; then
    mkfs.ext4 -F -L "rootfs" ${PARTITION2}
else
    echo "错误: /dev下找不到 SD卡 rootfs分区"
fi

while [ ! -e $device ]
do
sleep 1
echo "wait for $device appear"
done

echo "正在烧写${Uboot}到${device}"
execute "dd if=$sdkdir/boot/$Uboot of=$device bs=1024 seek=1 conv=fsync"
sync
echo "烧写${Uboot}到${device}完成!"

echo "正在准备复制..."
echo "正在复制设备树与内核到${device}1,请稍候..."
execute "mkdir -p /tmp/sdk/$$"
execute "mount ${device}1 /tmp/sdk/$$"
execute "cp -r $sdkdir/boot/*${flash}*.dtb /tmp/sdk/$$/"
execute "cp -r $sdkdir/boot/zImage /tmp/sdk/$$/"
#execute "cp $sdkdir/boot/alientek.bmp /tmp/sdk/$$/"
sync
echo "复制设备树与内核到${device}1完成!"

if [ "$copy" != "" ]; then
  echo "Copying additional file(s) on ${device}p1"
  execute "cp -r $copy /tmp/sdk/$$"
fi

echo "卸载${device}1"
execute "umount /tmp/sdk/$$"
sleep 1
#解压文件系统到文件系统分区
#挂载文件系统分区
execute "mkdir -p /tmp/sdk/$$"
execute "mount ${device}2 /tmp/sdk/$$"

echo "正在解压文件系统到${device}2 ,请稍候..."
rootfs=`ls -1 filesystem/ *.tar.*`
execute "tar jxfm $rootfs -C /tmp/sdk/$$"
sync
echo "解压文件系统到${device}2完成!"
...

It can be seen that the work of the script is roughly divided into three steps:

  1. Clear data from SD card

  2. Format the SD card into two areas. The first partition is 64M, which is used to store the device tree and kernel image files. The partition is in Fat32 format. The second partition is the total size of the SD card - 64M, which is used to store the file system. ext4 format

  3. Copy the system files to the partition corresponding to the sd card, u-boot-imx6ull-14x14-ddr512-emmc.imx, zImage, imx6ull-14x14-emmc-10.1-1280x800-c.dtb, rootfs.tar.bz2

Well, after understanding the general content of the script, we need to modify it according to our own actual system files. The system files are as follows:

u-boot-dtb.imx
zImage
imx6ull-toto.dtb
rootfs.tar.bz2

The modified content of the script imx6mksdboot.sh is as follows:

#! /bin/sh
#I.MX6 SD卡启动系统烧写脚本
#版本v1.0
#Author:ALIENTEK
VERSION="1.0"
#打印用法
usage ()
{
  echo "

用法: `basename $1` [选项] <(必选)-device> <(可选)-flash> <(可选)-ddrsize>
用法示例:
sudo ./imx6mksdboot.sh -device /dev/sdd
sudo ./imx6mksdboot.sh -device /dev/sdd -flash emmc -ddrsize 512 
命令选项:
  -device              SD卡块设备节点 (例如/dev/sdx)
  -flash               请选择开发板Flash类型(emmc | nand)
  -ddrsize             请选择DDR大小 (512 | 256) 
可选选项:
  --version             打印版本信息.
  --help                打印帮助信息.
"
  exit 1
}
#Uboot默认值
Uboot='u-boot-dtb.imx'

#execute执行语句成功与否打印
execute ()
{
    $* >/dev/null
    if [ $? -ne 0 ]; then
        echo
        echo "错误: 执行 $*"
        echo
        exit 1
    fi
}

#打印版本信息
version ()
{
  echo
  echo "`basename $1` version $VERSION"
  echo "I.MX6 SD卡制卡脚本"
  echo

  exit 0
}

#判断参数个数
arg=$#
if [ $arg -ne 6 ];then
number=1

while [ $# -gt 0 ]; do
  case $1 in
    --help | -h)
      usage $0
      ;;
    -device) shift; device=$1; shift; ;;
    --version) version $0;;
    *) copy="$copy $1"; shift; ;;
  esac
done
#判断字符串是否为零
test -z $device && usage $0
echo ""
echo "根据下面的提示,补全缺省的参数-flash -ddrsize"
read -p "请选择开发板参数,输入数字1~4,按Enter键确认
1.-flash emmc,-ddrsize 512
2.-flash emmc,-ddrsize 256
3.-flash nand,-ddrsize 512
4.-flash nand,-ddrsize 256 
输入数字1~4(default 1): " number
if [ -z $number ];then
  echo "使用默认参数:EMMC版本,DDR大小为512MB"
else
  case $number in
    1)  echo '您已经选择开发板参数为:EMMC版本,DDR大小为512MB'
        Uboot='u-boot-dtb.imx'
    ;;
    2)  echo '您已经选择开发板参数为:EMMC版本,DDR大小为256MB'
        Uboot='u-boot-imx6ull-14x14-ddr256-emmc.imx'
    ;;
    3)  echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为512MB'
        Uboot='u-boot-imx6ull-14x14-ddr512-nand-sd.imx'
    ;;
    4)  echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为256MB'
        Uboot='u-boot-imx6ull-14x14-ddr256-nand-sd.imx'
    ;;
    *)  echo '输入的参数有误,退出制卡';exit;
    ;;
esac
fi
else
#命令行处理,根据选项获得参数
while [ $# -gt 0 ]; do
  case $1 in
    --help | -h)
      usage $0
      ;;
    -device) shift; device=$1; shift; ;;
    -flash) shift; flash=$1; shift; ;;
    -ddrsize) shift; ddrsize=$1; shift; ;;
    --version) version $0;;
    *) copy="$copy $1"; shift; ;;
  esac
done
  if [ $flash = "emmc" -a $ddrsize = "512" ];then
    Uboot='u-boot-dtb.imx'
    echo '您已经选择开发板参数为:EMMC版本,DDR大小为512MB'

  elif [ $flash = "emmc" -a $ddrsize = "256" ];then
    Uboot='u-boot-imx6ull-14x14-ddr256-emmc.imx'
    echo '您已经选择开发板参数为:EMMC版本,DDR大小为256MB'

  elif [ $flash = "nand" -a $ddrsize = "512" ];then
    Uboot='u-boot-imx6ull-14x14-ddr512-nand-sd.imx'
    echo '您已经选择开发板参数为:NAND FLASH版本,DDR大小为512MB'

  elif [ $flash = "nand" -a $ddrsize = "256" ];then
    Uboot='u-boot-imx6ull-14x14-ddr256-nand-sd.imx'
    echo '您的开发板参数为:NAND FLASH版本,DDR大小为256MB'
  else
    echo '参数有误!'
    usage $0
  fi 
fi

#测试制卡包当前目录下是否缺失制卡所需要的文件
sdkdir=$PWD

if [ ! -d $sdkdir ]; then
   echo "错误: $sdkdir目录不存在"
   exit 1
fi

if [ ! -f $sdkdir/filesystem/ *.tar.* ]; then
  echo "错误: $sdkdir/filesystem/下找不到文件系统压缩包"
  exit 1
fi

if [ ! -f $sdkdir/boot/zImage ]; then
  echo "错误: $sdkdir/boot/下找不到zImage"
  exit 1
fi
#判断选择的块设备是否存在及是否是一个块设备
if [ ! -b $device ]; then
  echo "错误: $device 不是一个块设备文件"
  exit 1
fi
#这里防止选错设备,否则会影响Ubuntu系统的启动
if [ $device = '/dev/sda' ];then
  echo "请不要选择sda设备,/dev/sda通常是您的Ubuntu硬盘!
继续操作你的系统将会受到影响!脚本已自动退出"
  exit 1 
fi
echo "即将进行制作SD系统启动卡,大约花费几分钟时间,请耐心等待!"
echo "************************************************************"
echo "*         注意:这将会清除$device所有的数据               *"
echo "*         在脚本执行时请不要将$device拔出                 *"
echo "*             请按<Enter>确认继续                          *"
echo "************************************************************"
read enter

#格式化前要卸载
for i in `ls -1 $device?`; do
 echo "卸载 device '$i'"
 umount $i 2>/dev/null
done

#执行格式化$device
execute "dd if=/dev/zero of=$device bs=1024 count=1024"

#第一个分区为64M用来存放设备树与内核镜像文件,因为设备树与内核都比较小,不需要太大的空间
#第二个分区为SD卡的总大小-64M,用来存放文件系统
cat << END | fdisk -H 255 -S 63 $device
n
p
1

+64M
n
p
2


t
1
c
a
1
w
END

#两个分区处理
PARTITION1=${device}1
if [ ! -b ${PARTITION1} ]; then
        PARTITION1=${device}1
fi

PARTITION2=${device}2
if [ ! -b ${PARTITION2} ]; then
        PARTITION2=${device}2
fi

#第一个分区创建为Fat32格式
echo "格式化 ${device}1 ..."
if [ -b ${PARTITION1} ]; then
        mkfs.vfat -F 32 -n "boot" ${PARTITION1}
else
        echo "错误: /dev下找不到 SD卡 boot分区"
fi
#第二个分区创建为ext4格式
echo "格式化${device}2 ..."
if [ -b ${PARITION2} ]; then
        mkfs.ext4 -F -L "rootfs" ${PARTITION2}
else
        echo "错误: /dev下找不到 SD卡 rootfs分区"
fi

while [ ! -e $device ]
do
sleep 1
echo "wait for $device appear"
done

echo "正在烧写${Uboot}到${device}"
execute "dd if=$sdkdir/boot/$Uboot of=$device bs=1024 seek=1 conv=fsync"
sync
echo "烧写${Uboot}到${device}完成!"

echo "正在准备复制..."
echo "正在复制设备树与内核到${device}1,请稍候..."
execute "mkdir -p /tmp/sdk/$$"
execute "mount ${device}1 /tmp/sdk/$$"
execute "cp -r $sdkdir/boot/imx6ull-toto.dtb /tmp/sdk/$$/"
execute "cp -r $sdkdir/boot/zImage /tmp/sdk/$$/"
#execute "cp $sdkdir/boot/alientek.bmp /tmp/sdk/$$/"
sync
echo "复制设备树与内核到${device}1完成!"

if [ "$copy" != "" ]; then
  echo "Copying additional file(s) on ${device}p1"
  execute "cp -r $copy /tmp/sdk/$$"
fi

echo "卸载${device}1"
execute "umount /tmp/sdk/$$"
sleep 1
#解压文件系统到文件系统分区
#挂载文件系统分区
execute "mkdir -p /tmp/sdk/$$"
execute "mount ${device}2 /tmp/sdk/$$"

echo "正在解压文件系统到${device}2 ,请稍候..."
rootfs=`ls -1 filesystem/ *.tar.*`
execute "tar jxfm $rootfs -C /tmp/sdk/$$"
sync
echo "解压文件系统到${device}2完成!"

#判断是否存在这个目录,如果不存在就为文件系统创建一个modules目录
if [ ! -e "${device}p2/lib/modules/" ];then
mkdir -p ${device}p2/lib/modules/
fi

#echo "正在解压模块到${device}2/lib/modules/ ,请稍候..."
#modules=`ls -1 modules/ *.tar.*`
#execute "tar jxfm $modules -C /tmp/sdk/$$/lib/modules/"
#sync
#echo "解压模块到${device}2/lib/modules/完成!"

echo "卸载${device}2"
execute "umount /tmp/sdk/$$"

execute "rm -rf /tmp/sdk/$$"
sync
echo "SD卡启动系统烧写完成!"
  1. Modify lines 81 and 111 to "Uboot='u-boot-dtb.imx'"

  2. Modify line 238 to execute "cp -r $sdkdir/boot/imx6ull-toto.dtb /tmp/sdk/$$/" 

  3. Mask lines 268 to 272 and then all we need to do is put our own system files into the corresponding folder:

u-boot-dtb.imx      ---> 拷贝到boot文件夹
zImage              ---> 拷贝到boot文件夹
imx6ull-toto.dtb    ---> 拷贝到boot文件夹
rootfs.tar.bz2      ---> 拷贝到filesystem文件夹

Okay, the preparation work has been completed. The next is the most important work, which is to burn the system to the sd card and burn the system files to the sd card for direct execution./imx6mksdboot.sh --help View how to use the script:

toto@toto:~/workspace/imx6mksdboot/files$ ./imx6mksdboot.sh --help

  
用法: imx6mksdboot.sh [选项] <(必选)-device> <(可选)-flash> <(可选)-ddrsize>
用法示例:
sudo ./imx6mksdboot.sh -device /dev/sdb
sudo ./imx6mksdboot.sh -device /dev/sdb -flash emmc -ddrsize 512 
命令选项:
  -device              SD卡块设备节点 (例如/dev/sdx)
  -flash               请选择开发板Flash类型(emmc | nand)
  -ddrsize             请选择DDR大小 (512 | 256) 
可选选项:
  --version             打印版本信息.
  --help                打印帮助信息.

Instructions for use:

用法: imx6mksdboot.sh [选项] <(必选)-device> <(可选)-flash> <(可选)-ddrsize>
  • -device: Specifies the device node (the node mounted on the SD card such as /dev/sdx). This parameter must be added when executing the script.

  • -flash: Specify the media storage medium on the core board, optional (emmc|nand)

  • -ddrsize: Specify the DDR capacity size on the core board, optionally (512|256) MB. For example, the current user's DDR capacity size on the core board is 512MB, and the media storage medium is eMMC. The SD card mounting node is /dev/sdb.

Then the instructions to solidify the SD card are as follows. After executing the instructions, the script will have Chinese characters and ask again whether the node mounted on the SD card corresponds to it. All data on the SD card will be cleared. Please be careful to back up important data. Press the Enter key to confirm and continue. It will take about a few minutes to solidify the SD card. Depending on the personal computer and the SD card used, the time it takes may vary greatly.

Run the script imx6mksdboot.sh to burn the system files. The specific commands are as follows:

sudo ./imx6mksdboot.sh -device /dev/sdb -flash emmc -ddrsize 512

Note: Please operate /dev/sdb according to your actual situation. You can enter "sudo fdisk -l" to view the SD card mounting node.

toto@toto:~/workspace/imx6mksdboot/files$ sudo ./imx6mksdboot.sh -device /dev/sdb -flash emmc -ddrsize 512
[sudo] toto 的密码: 
您已经选择开发板参数为:EMMC版本,DDR大小为512MB
即将进行制作SD系统启动卡,大约花费几分钟时间,请耐心等待!
************************************************************
*         注意:这将会清除/dev/sdb所有的数据               *
*         在脚本执行时请不要将/dev/sdb拔出                 *
*             请按<Enter>确认继续                          *
************************************************************

Press the "Enter" key to continue. During curing, there will be a combination of Chinese and English to prompt the curing process. The curing process is completed as shown below:

卸载 device '/dev/sdb1'
卸载 device '/dev/sdb2'
记录了1024+0 的读入
记录了1024+0 的写出
1048576 bytes (1.0 MB, 1.0 MiB) copied, 3.19621 s, 328 kB/s

欢迎使用 fdisk (util-linux 2.31.1)。
更改将停留在内存中,直到您决定将更改写入磁盘。
使用写入命令前请三思。

设备不包含可识别的分区表。
创建了一个磁盘标识符为 0xbaabddd3 的新 DOS 磁盘标签。

命令(输入 m 获取帮助): 分区类型
   p   主分区 (0个主分区,0个扩展分区,4空闲)
   e   扩展分区 (逻辑分区容器)
选择 (默认 p): 分区号 (1-4, 默认  1): 第一个扇区 (2048-31116287, 默认 2048): 上个扇区,+sectors 或 +size{K,M,G,T,P} (2048-31116287, 默认 31116287): 
创建了一个新分区 1,类型为“Linux”,大小为 64 MiB。
分区 #1 包含一个 vfat 签名。

命令(输入 m 获取帮助): 分区类型
   p   主分区 (1个主分区,0个扩展分区,3空闲)
   e   扩展分区 (逻辑分区容器)
选择 (默认 p): 分区号 (2-4, 默认  2): 第一个扇区 (133120-31116287, 默认 133120): 上个扇区,+sectors 或 +size{K,M,G,T,P} (133120-31116287, 默认 31116287): 
创建了一个新分区 2,类型为“Linux”,大小为 14.8 GiB。
分区 #2 包含一个 ext4 签名。

命令(输入 m 获取帮助): 分区号 (1,2, 默认  2): Hex 代码(输入 L 列出所有代码): 
已将分区“Linux”的类型更改为“W95 FAT32 (LBA)”。

命令(输入 m 获取帮助): 分区号 (1,2, 默认  2): 
分区 1 的 可启动 标志已启用。

命令(输入 m 获取帮助): 分区表已调整。
将调用 ioctl() 来重新读分区表。
正在同步磁盘。

格式化 /dev/sdb1 ...
mkfs.fat 4.1 (2017-01-24)
mkfs.fat: warning - lowercase labels might not work properly with DOS or Windows
格式化/dev/sdb2 ...
mke2fs 1.44.1 (24-Mar-2018)
/dev/sdb2 有一个标签为“rootfs”的 ext4 文件系统
        上一次挂载于 /tmp/sdk/26687, 时间 Mon Jun 12 22:36:06 2023
创建含有 3872896 个块(每块 4k)和 969136 个inode的文件系统
文件系统UUID:4bdc82c7-5e83-4992-9966-cd99a2317944
超级块的备份存储于下列块: 
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208

正在分配组表: 完成                            
正在写入inode表: 完成                            
创建日志(16384 个块) 完成
写入超级块和文件系统账户统计信息: 已完成 

正在烧写u-boot-dtb.imx到/dev/sdb
记录了407+0 的读入
记录了407+0 的写出
416768 bytes (417 kB, 407 KiB) copied, 1.70141 s, 245 kB/s
烧写u-boot-dtb.imx到/dev/sdb完成!
正在准备复制...
正在复制设备树与内核到/dev/sdb1,请稍候...
复制设备树与内核到/dev/sdb1完成!
卸载/dev/sdb1
正在解压文件系统到/dev/sdb2 ,请稍候...
解压文件系统到/dev/sdb2完成!
卸载/dev/sdb2
SD卡启动系统烧写完成!

Set the DIP switch on the development board to 10000010, start the system from the SD card, insert the SD card that has just programmed the system, and power on to start the system.

Set the two environment variables bootargs and bootcmd under u-boot. The commands are as follows:

setenv bootcmd 'mmc dev 0; fatload mmc 0:1 80800000 zImage; fatload mmc 0:1 83000000 imx6ull-toto.dtb; bootz 80800000 - 83000000;'
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk0p2 rootwait rw'

saveenv

Note: If you don’t know “mmc dev 0”, you can enter the command “mmc list” to check which is the SD card and which is the eMMC, as follows:

=> mmc list
FSL_SDHC: 0 (SD)
FSL_SDHC: 1

The meaning of bootcmd is as follows:

mmc dev 0 //切换到 SD卡
fatload mmc 0:1 0x80800000 zImage //从SD卡分区1中读取 zImage 到 0x80800000 处
fatload mmc 0:1 0x83000000 imx6ull-14x14-evk.dtb //从SD卡分区1中读取设备树到 0x83000000 处
bootz 0x80800000 - 0x83000000 //启动 Linux

The meaning of bootargs is as follows:

  • Console: Used to set up the Linux terminal (or console), that is, what device is used to interact with Linux, is it a serial port or an LCD screen? If it is a serial port, it should be the serial port number and so on. Generally, the serial port is set as a Linux terminal, so that we can interact with Linux through SecureCRT on the computer. The console is set to ttymxc0 here, because after Linux is started, the device file of I.MX6ULL serial port 1 under Linux is /dev/ttymxc0. Under Linux, everything is a file. There is ",115200" behind ttymxc0, which is to set the baud rate of the serial port. console=ttymxc0,115200. In summary, it sets ttymxc0 (that is, serial port 1) as the Linux terminal, and the serial port baud rate is set to 115200.

  • rootroot: used to set the location of the root file system, root=/dev/mmcblk0p2 is used to indicate that the root file system is stored in partition 2 of the mmcblk0 device. After the EMMC version of the core board starts Linux, there will be files such as /dev/mmcblk0, /dev/mmcblk1, /dev/mmcblk0p1, /dev/mmcblk0p2, /dev/mmcblk1p1 and /dev/mmcblk1p2, among which /dev/mmcblkx(x =0~n) means mmc device, and /dev/mmcblkxpy(x=0~n,y=1~n) means partition y of mmc device x. In the I.MX6U-ALPHA development board, /dev/mmcblk0 means the SD card, and /dev/mmcblk1p2 means the partition 2 of the SD card. There is "rootwait rw" after root. Rootwait means waiting for the mmc device to be initialized before mounting. Otherwise, an error will occur if the root file system is mounted before the mmc device is initialized. rw means that the root file system can be read and written. Without rw, it may not be possible to perform write operations in the root file system, and only read operations can be performed.

  • rootfstype: This option is generally used with root configuration. rootfstype is used to specify the root file system type. This option does not matter if the root file system is in ext format. If the root file system is yaffs, jffs or ubifs, you need to set this option to specify the type of root file system. The system mounts the root file system successfully, and the printed information is as follows:

[    2.502135]   No soundcards found.
[    2.533819] EXT4-fs (mmcblk0p2): mounted filesystem 4bdc82c7-5e83-4992-9966-cd99a2317944 with ordered data mode. Quota mode: none.
[    2.570874] VFS: Mounted root (ext4 filesystem) on device 179:2.
[    2.581547] devtmpfs: mounted
[    2.586663] Freeing unused kernel image (initmem) memory: 1024K
[    2.593232] Run /sbin/init as init process
[    2.718227] usb 1-1: new high-speed USB device number 2 using ci_hdrc
can't run '/etc/init.d/rcS': No such file or directory

You can see the last line "can't run '/etc/init.d/rcS': No such file or directory", indicating that the file and directory do not exist and cannot be run. It shows that the root file system still lacks some files, which will be improved later.

5.3 eMMC mount rootfs

The eMMC mounting method is the same as that of the SD card, so I won’t explain it in detail here. The steps are as follows:

  1. Format partitions, the first partition boot, Fat32 format, used to store u-boot-toto.imx, zImage, imx6ull-toto.dtb; the second partition rootfs, ext4 format, used to store the root file system

  2. Set two environment variables bootcmd and bootargs under u-boot

6. Improve the root file system

6.1 Create the /etc/init.d/rcS file

rcS is a shell script. After the Linux kernel is started, some services need to be started, and rcS is a script file that specifies which files to start. Create the /etc/init.d/rcS file in rootfs, and then enter the following content in rcS:

#!/bin/sh
 
PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
export PATH LD_LIBRARY_PATH
        
mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts
 
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
  • Line 1 indicates that this is a shell script.

  • In line 3, the PATH environment variable stores the directory where the executable file may exist, so that when we execute some commands or executable files, we will not be prompted with errors such as file not found.

  • Line 4, the LD_LIBRARY_PATH environment variable saves the directory where the library file is located.

  • On line 5, use export to export the above environment variables, which is equivalent to declaring some "global variables".

  • On line 7, use the mount command to mount all file systems. These file systems are specified by the file /etc/fstab, so we will create the /etc/fstab file later.

  • Lines 8 and 9 create the directory /dev/pts and then mount devpts into the /dev/pts directory.

  • Lines 11 and 12 use mdev to manage hot-plug devices. Through these two lines, the Linux kernel can automatically create device nodes in the /dev directory. For details about mdev, please refer to the docs/mdev.txt document in busybox.

The content of the rcS file in the above example is the most concise. If you have a certain understanding of the rcS file in Ubuntu or other large Linux operating systems, you will find that it is very complicated. Now we are just learning for the first time, so there is no need to make it so complicated. Just know the function and location of the file. In subsequent work, such complex rcS files will also be created with the help of other tools, such as buildroot, etc.

After creating the file /etc/init.d/rcS, you must give it executable permission! The command is as follows:

chmod 777 /etc/init.d/rcS 

Restart the Linux kernel, and the printed information after startup is as follows:

mount: can't read '/etc/fstab': No such file or directory
/etc/init.d/rcS: line 11: can't create /proc/sys/kernel/hotplug: nonexistent directory
mdev: /sys/dev: No such file or directory

Please press Enter to activate this console.

It prompts that the /etc/fstab file cannot be found, and there are some other errors. Let’s solve the /etc/fstab error first. We mentioned earlier that "mount -a" needs to read /etc/fstab when mounting all root file systems, because /etc/fstab defines which files should be mounted. The next step is to create the /etc/fstab file.

6.2 Create the /etc/fstab file

Create the /etc/fstab file and enter the following in the fstab file:

/ # vi /etc/fstab

#<file system>  <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults        0       0
tmpfs           /tmp            tmpfs   defaults        0       0
sysfs           /sys            sysfs   defaults        0       0

The fstab format is as follows:

<file system> <mount point> <type> <options> <dump> <pass>
  • [file system]: The special device to be mounted, it can also be a block device, such as /dev/sda and so on.

  • [mount point]: Mount point.

  • [type]: file system type, such as ext2, ext3, proc, romfs, tmpfs, etc.

  • [options]: Mount options. Enter the "man mount" command in Ubuntu to view specific options. Generally, defaults are used, which is the default options. Defaults includes rw, suid, dev, exec, auto, nouser and async.

  • [dump]: If it is 1, it means backup is allowed, if it is 0, no backup, generally no backup, so set it to 0.

  • [pass]: Disk check setting, 0 means no check. The root directory '/' is set to 1, other partitions cannot be set to 1, and other partitions start from 2. Generally, the root directory is not mounted in fstab, so it is generally set to 0 here.

After the fstab file is created, restart the development board and the printed information is as follows:

VFS: Mounted root (ext4 filesystem) on device 179:26.
[    2.556792] devtmpfs: mounted
[    2.561310] Freeing unused kernel image (initmem) memory: 1024K
[    2.567854] Run /sbin/init as init process
[    2.718088] usb 1-1: new high-speed USB device number 2 using ci_hdrc
/etc/init.d/rcS: line 11: can't create /proc/sys/kernel/hotplug: nonexistent directory

It still prompts "/etc/init.d/rcS: line 11: can't create /proc/sys/kernel/hotplug: nonexistent directory". From the online search, I learned that CONFIG_HOTPLUG=y was not selected when compiling the kernel, so No hotplug files will be created under /proc/sys/kernel (see kernel/sysctl.c). Modify the configuration of the kernel, select the macro CONFIG_HOTPLUG. Continue adding other files.

6.3 Create the /etc/inittab file

Create the /etc/inittab file and enter the following content in the inittab file:

::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
::restart:/sbin/init 
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
  • Line 2, run the script file /etc/init.d/rcS after the system starts.

  • Line 3, use console as a console terminal, which is ttymxc0

  • Line 4, if you reboot, run /sbin/init

  • Line 5, if you press the ctrl+alt+del key combination, /sbin/reboot will run. It seems that the ctrl+alt+del key combination is used to restart the system.

  • Line 6, execute /bin/umount when shutting down, which means uninstalling each file system

  • Line 7, execute /sbin/swapoff when shutting down, which means closing the swap partition.

For details about inittab, please refer to the file examples/inittab under busybox. The init program will read the file /etc/inittab. inittab consists of several instructions. The structure of each instruction is the same, consisting of 4 segments separated by ":", with the following format:

<id>:<runlevels>:<action>:<process>
  • [id]: The identifier of each instruction, which cannot be repeated. But for busybox's init, it has special meaning. For busybox, it is used to specify the control tty to start the process. Generally, we set the serial port or LCD screen as the control tty.

  • [runlevels]: This item is completely useless for busybox, so leave it empty

  • [action]: action, used to specify the action that may be used

  • [process]: Specific actions, such as programs, scripts or commands.

The actions supported by busybox are as follows:

action describe
sysinit The process will only be executed once during system initialization.
respawn When the process terminates, start a new one immediately
askfirst Similar to respawn, "Please press Enter to activate this console." is displayed on the console before running the process. The process will only be executed as long as the user presses the "Enter" key.
wait Tell init to wait for the corresponding process to finish executing before continuing.
once Only execute once and will not wait for the process to complete.
restart Proceec will only be executed when init is restarted.
trlalt part The process will only be executed when the ctrl+alt+del key combination is pressed.
shutdown Execute process when shutting down

So far! All the files to be created in the root file system have been completed. Next, we need to conduct other tests on the root file system, such as whether the software we wrote ourselves runs normally, whether it supports automatic startup of the software, whether Chinese support is normal, and whether it can be linked, etc.

7. Root file system function test

7.1 Application running test

The purpose of using Linux is to run our own software. The application software we compile generally uses a dynamic library. If the dynamic library is used, the application software is small in size, but the library file must be provided. We have added the library file to the root file system. . We write a small test software to test whether the library files are working properly, create a folder named "drivers" under the root file system, and put all the experimental files in this file when we learn Linux drivers in the future clip inside.

Use the vim editor under ubuntu to create a new hello.c file and enter the following content in hello.c:

#include <stdio.h>

int main(void)
{
    while(1)
    {
        printf("hello world!\r\n");
        sleep(2);
    }
    return 0;
}

Because we want to run on the development board, we need to use a cross-compiler to compile. The specific compilation commands are as follows:

arm-linux-gnueabihf-gcc hello.c -o hello

Copy it to the rootfs/drivers directory and enter the following command on the development board to execute the executable file:

cd /drivers //进入 drivers 目录
./hello //执行 hello

The program runs normally, indicating that the shared library in our root file system is OK.

7.2 Chinese character test

Create a new folder named "Chinese Test" on the development board, and then check whether the Chinese name can be displayed correctly under SecureCRT. Enter the "ls" command, the results are as follows:

/ # mkdir 中文测试
/ # ls
bin           lib           mnt           sbin          usr
dev           linuxrc       proc          sys           中文测试
etc           lost+found    root          tmp
/ # 

It means that the root file system already supports Chinese.

7.3 Auto-start test after power on

Add the hello program to the /etc/init.d/rcS file to start it automatically at boot. The content is as follows:

#!/bin/sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
export PATH LD_LIBRARY_PATH

mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts

echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
         
./hello &

After the self-startup code is added, the development board can be restarted, and the hello will automatically run after booting, indicating that the booting self-starting is successful.

This is the end of today's content, thank you for watching

Guess you like

Origin blog.csdn.net/weixin_41114301/article/details/132775312