给Jetson Nano更换eMMC闪存【下篇 - 一些问题的解释和探讨】

7月8日更新:添加了硬件部分的视频链接:b站BV11w411R77n

空间不足?给Jetson Nano更换emmc硬盘(扩容)_哔哩哔哩_bilibili


【声明】我还只是个Linux小白,写的东西并不一定对,仅提供一种参考思路,请各路大神莫喷,有想法可以相互交流。

【邮箱】188283942@qq.com

1  前言

        在使用Jetson Nano的过程中,遇到了16GB板载存储空间不足的情况,于是我选择了更换板载emmc闪存,方法参见我上一篇文章:

        给Jetson Nano更换eMMC闪存(扩容

        具体的步骤这里就不在详述,上一篇文章已经写了,大家可以回顾一下上一篇文章。这里主要探讨那些“为什么要这么做”、“为什么改这里就行”的问题。

        这篇文章写的可能比较乱,因为好多问题我暂时也没搞清楚,都是随性地写。如果你也遇到了类似的问题,不妨仔细读一下,也许对你有一定的参考意义。   

2  问题的探讨

2.1 内容回顾

        上一篇文章已经提到,要更换emmc,就需要修改BSP(board support package)支持包中的一些配置文件,这些配置文件会被拷贝到Linux_for_Tegra(可以理解为刷机工具)目录下,在运行刷机工具(即flash.sh)时,会根据这些配置文件生成nano的分区表,然后才将系统镜像刷入。

        这里需要探讨的就是:如果从硬件上更换emmc,而刷机时不修改相应分区表配置,会出现什么问题?为什么要这样(按上一篇文章的方法)修改配置文件?

2.2 不修改配置文件直接刷机,会遇到的问题

        一开始,我将emmc更换后,抱着侥幸心理直接刷的机(没有修改配置文件,觉得它能够自动适应不同size的emmc),结果就是无法进入系统,具体就是:

A start job is running for end-user configuration after initial OEM installation

        一直没能进入系统,百度了下,有说是swap的问题,也有说是硬盘的uuid和fstab(分区表)中的uuid不一致造成的。

        因此我就怀疑是分区表的问题,如果不修改分区表相应配置,就不能适应新更换的emmc闪存。后续解决问题的步骤其实都是围绕着分区表来进行的。

2.3 尝试从系统镜像入手,修改分区配置

        通过刷机过程(包括初次刷机、镜像恢复)可以了解到,刷机环境准备好后,会在Linux_for_Tegra/bootloader目录下生成system.img(我这里是4.8G),刷机应该就是将这个镜像刷写到emmc中(或者sd卡、nvme硬盘)。其实就有点类似树莓派给sd卡烧写系统一样。

        生成的img镜像应该是包含了分区信息的,但是我弄的时候遇到些错误,我就没接着弄下去,如果你有兴趣的话可以试试这种方法(直接通过img修改各分区的大小)。

2.4 修改分区表相关配置参数

        我没有直接对system.img文件下手,而是直接通过修改生成分区表所依赖的配置文件,来修改所生成的分区表(有一点点绕哈)。

        在官方的手册《NVIDIA Jetson Linux Developer Guide》-> Bootloader -> Partition Configureation 章节中,详细叙述了nano刷机过程中有关分区表的相关事项,包括分区表的生成方式,分区表相关配置文件的解析(释义)等内容。

        该章节中有一段话说明了分区表是如何生成的:

        During the flashing procedure, flash.sh reads in the partition configuration file, translates keywords into values specified in <device>.conf or in option parameters and saves the data in bootloader/flash.xml. Then bootloader/tegraflash.py reads in bootloader/flash.xml and performs actual flashing as specified by bootloader/flash.xml.

        翻译过来就是说:刷机时,flash.sh(上一篇文章刷机过程有提到)会读取分区表配置文件,将<device>.conf中所指定的参数读取出来,将其保存到bootloader/flash.xml文件中,然后bootloader/tegraflash.py读取flash.xml文件中的参数,按照指定的参数执行刷写操作。

        说白了,分区表的配置信息,就是从.conf配置文件,流向flash.xml文件,最后tegraflash.py读取,并执行刷机。那么我们下一步的目标,就是关注.conf配置文件和flash.xml文件,是如何生成,或者如何修改的。

        上一篇文章中已经提到,在BSP支持包的安装部分,需要执行install.sh,这个脚本会将Realtimes_L4T_3243_nano_v1.5.tar.gz(包含有和分区表配置相关的配置文件,也就是.conf文件)中的相关文件,写入到Linux_for_Tegra文件夹中,并运行/Linux_for_Tegra/apply_binaries.sh,应用BSP支持包的相关配置。不同的硬件,安装BSP支持包的方式可能不一样,但重点都是关注这里的.conf文件。

        那么,这个.conf文件是什么样的?都有哪些参数?它是如何影响分区表配置的?下面我们来看rtso6001-b.conf文件(因为我的载板是RTSO-6001b,商家提供的BSP包里头就是这么命名的):

EMMC_CFG=flash_l4t_t210_emmc_p3448.xml;
BLBlockSize=1048576;
source "${LDK_DIR}/rtso6001-b.conf.common";
T21BINARGS="--bins \"EBT cboot.bin; "

ROOTFSSIZE=14GiB;
VERFILENAME="emmc_bootblob_ver.txt";

        EMMC_CFG - 应该是指定了emmc的配置文件,这里的flash_l4t_t210_emmc_p3448.xml文件位于Linux_for_Tegra/bootloader/t210ref/cfg目录下。

        BLBlockSize - bootloader block size应该是bootloader相关,不太懂,就不去动他了。

        source "${LDK_DIR}/rtso6001-b.conf.common" - 应该是指定了.conf文件对应的.common文件。

        T21BINARGS - 不知道是啥。

        ROOTFSSIZE    - 根分区大小,也就是上一篇文章中说到的,要修改的参数,比如55GiB。

        VERFILENAME - 校验文件,不用管它。             

        接下来我们就来看看这里提到的flash_l4t_t210_emmc_p3448.xml文件(其实你会发现这个文件和上面提到的flash.xml文件基本是一样的,或者说,后者源于前者),其实这就是一个分区配置表,具体的含义在《NVIDIA Jetson Linux Developer Guide》-> Bootloader -> Partition Configureation 章节中有叙述。

<?xml version="1.0"?>

<!-- Nvidia Tegra Partition Layout Version 1.0.0 -->

<partition_layout version="01.00.0000">
    <device type="sdmmc" instance="3">
        <partition name="BCT" id="2" type="boot_config_table">
            <allocation_policy> sequential </allocation_policy>
            <filesystem_type> basic </filesystem_type>
            <size> 1048576 </size>
            <allocation_attribute> 8 </allocation_attribute>
            <description> **Required.** Contains Boot Configuration Table (BCT). </description>
        </partition>

        <partition name="NXC" id="3" type="NVCTYPE">
            <allocation_policy> sequential </allocation_policy>
            <filesystem_type> basic </filesystem_type>
            <size> 262144 </size>
            <allocation_attribute> 8 </allocation_attribute>
            <filename> NVCFILE </filename>
            <description> **Required.** Contains TegraBoot binary. </description>
        </partition>

        <partition name="PT" id="4" type="partition_table">
            <allocation_policy> sequential </allocation_policy>
            <filesystem_type> basic </filesystem_type>
            <size> 131072 </size>
            <allocation_attribute> 8 </allocation_attribute>
            <filename> flash.xml.bin </filename>
            <description> **Required.** Contains Partition Table. </description>
        </partition>

<!-- 省略号 -->

        <partition name="APP" id="23" type="data">
            <allocation_policy> sequential </allocation_policy>
            <filesystem_type> basic </filesystem_type>
            <size> APPSIZE </size>
            <allocation_attribute> 8 </allocation_attribute>
            <unique_guid> APPUUID </unique_guid>
            <filename> APPFILE </filename>
            <description> **Required.** Contains the rootfs. This partition must be defined after
              `primary_GPT` so it can be accessed as the fixed known special device
              `/dev/mmcblk0p1`. </description>
        </partition>

<!-- 省略号 -->

        可以看到,里面配置了许多分区,具体每一个分区是什么含义,可以看上面提到的手册。这里比如PT就是partition table,是存放分区表的,APP就是根分区,我们重点关注根分区的配置:

        allocation_policy - 值为sequential,就是说分区的分配是连续的(后面的接着前面的)。

        filesystem_type - basic这个不用管。

        size - 这里指定的值是APP_SIZE,我们下一步需要关注这个值是怎么来的。

        allocation_attribute - 指定为8。

        unique_guid - 指定的值为APPUUID,我没搞清楚这里指的是谁的uuid,是emmc的uuid?还是.img文件挂在为loop设备的uuid?还是别的什么?既然说开不了机可能和分区表中的uuid有关,那我们就需要关注一下它是从哪儿来的。

        filename - 指定为APPFILE,盲猜是指向system.img的路径。

        通过对这个xml文件可以看到,所有的分区都是连续分配的,也就是说我们只需要关注它的位置和大小,不需要关注起始点和终止点。那么如果要修改根分区的大小,只需要修改上面这个APP_SIZE的值(单位:Btye)就可以。

        注意到flash.sh中有以下几段代码和注释:

# Optional Environment Variables:
# ...
# BOOTPARTSIZE ----------- Total eMMC HW boot partition size.
# CFGFILE ---------------- Partition table configuration file to be used.
# CMDLINE ---------------- Target cmdline. See help for more information.
# DEVSECTSIZE ------------ Device Sector size. (default = 512Byte).
# DTBFILE ---------------- Device Tree file to be used.
# EMMCSIZE --------------- Size of target device eMMC (boot0+boot1+user).
# FLASHAPP --------------- Flash application running in host machine.
# FLASHER ---------------- Flash server running in target machine.
# INITRD ----------------- Initrd image file to be flashed.
# KERNEL_IMAGE ----------- Linux kernel zImage file to be flashed.
# ...
# ROOTFSSIZE ------------- Linux RootFS size (internal emmc/nand only).
# ROOTFS_DIR ------------- Linux RootFS directory name.

# ...

getsize ()
{
    local var="$1";
    local val="$2";
    if [[ ${!val} != *[!0-9]* ]]; then
        eval "${var}=${!val}";
    elif [[ (${!val} == *KiB) && (${!val} != *[!0-9]*KiB) ]]; then
        eval "${var}=$(( ${!val%KiB} * 1024 ))";
    elif [[ (${!val} == *MiB) && (${!val} != *[!0-9]*MiB) ]]; then
        eval "${var}=$(( ${!val%MiB} * 1024 * 1024 ))";
    elif [[ (${!val} == *GiB) && (${!val} != *[!0-9]*GiB) ]]; then
        eval "${var}=$(( ${!val%GiB} * 1024 * 1024 * 1024))";
    else
        echo "Error: Invalid $1: ${!val}";
        exit 1;
    fi;
}

setval     odmdata    ODMDATA;    # .conf mandatory
setval     rootfs_type    ROOTFS_TYPE;
setval     devsectsize    DEVSECTSIZE;
getsize    rootfssize    ROOTFSSIZE;    # .conf mandatory
getsize    emmcsize    EMMCSIZE;    # .conf mandatory
getsize    bootpartsize    BOOTPARTSIZE;    # .conf mandatory
getsize    bootpartlim    BOOTPARTLIMIT;
getsize    recrootfssize RECROOTFSSIZE;

        这个xml中APPSIZE具体是怎么来的,我确实没太搞清楚,因为我不太熟悉shell脚本,且这个脚本非常的长。根据前面的猜测,结合上面这些代码和注释,我认为flash.sh是将.conf文件中的ROOTFSSIZE关键字的值(原来是14GiB,我改成55GiB)读取过来,然后* 1024 * 1024 * 1024得到以byte为单位的值,并写入flash.xml(这个文件和flash_l4t_t210_emmc_p3448.xml基本是一样的)

        这一点,可以在执行flash.sh之后得到验证:flash.xml中APP分区(也就是根分区)的size变成了ROOTFSSIZE* 1024 * 1024 * 1024,比如55GiB就是59055800320,下面就是执行完flash.sh 之后flash.xml中APP分区的配置:

        <partition name="APP" id="23" type="data">
            <allocation_policy> sequential </allocation_policy>
            <filesystem_type> basic </filesystem_type>
            <size> 59055800320 </size>
            <allocation_attribute> 8 </allocation_attribute>
            <unique_guid>  </unique_guid>
            <filename> system.img </filename>
            <description> **Required.** Contains the rootfs. This partition must be defined after
              `primary_GPT` so it can be accessed as the fixed known special device
              `/dev/mmcblk0p1`. </description>
        </partition>

        而flash_l4t_t210_emmc_p3448.xml是这样的:

        <partition name="APP" id="23" type="data">
            <allocation_policy> sequential </allocation_policy>
            <filesystem_type> basic </filesystem_type>
            <size> APPSIZE </size>
            <allocation_attribute> 8 </allocation_attribute>
            <unique_guid> APPUUID </unique_guid>
            <filename> APPFILE </filename>
            <description> **Required.** Contains the rootfs. This partition must be defined after
              `primary_GPT` so it can be accessed as the fixed known special device
              `/dev/mmcblk0p1`. </description>
        </partition>

        所以这里的APPSIZE就是.conf文件中ROOTFSSIZE的值 * 1024 * 1024 * 1024,APPUUID似乎不需要管,APPFILE指的就是system.img。

        综上所述,可以得到这样的猜测:从.conf文件中读取了ROOTFSSIZE的值,然后将其* 1024 * 1024 * 1024 赋给APPSIZE,并获取APPUUID(值为空)、APPFILE(system.img)的值,这样flash_l4t_t210_emmc_p3448.xml就已经包含了分区表所需要的所有参数(APPSIZE、APPUUID、APPFILE这三个量都有了值),然后再将这个xml的内容给到flash.xml,然后据此生成分区表。

        嗯,上面的逻辑是不是有点乱,因为我也没搞清楚到底是怎么回事,总而言之,最终就是要修改flash.xml中APP分区(根分区)对应的这三个值,因为根据《NVIDIA Jetson Linux Developer Guide》中相应的内容(上面已经说过了),分区表就是根据flash.xml生成的。

       

        总的来说,我通过修改.conf文件中ROOTFSSIZE的值(14G改为55G),影响了flash.xml中APP分区(根分区)的大小,实现了根分区大小的调整,并且已经刷机成功,成功引导进入系统。

2.5 为什么只修改ROOTFSSIZE的值就OK了?

        我们现在已经知道,.conf中ROOTFSSIZE的值直接影响根分区的大小(所有分区都是顺序分配的)。

        在未修改这个值之前是14G,按此配置进行刷机,出现了前述的进不了系统的错误;在修改之后这个值是55G,它能够顺利刷入并正常进入系统。

        所以,为什么根分区的大小配置能够导致进不了系统的问题?

        嗯,我没搞明白,所以暂时给不出答案。如果要我猜测的话,根分区大小的配置,可能影响了其他分区的配置(因为分区是顺序配置的),才导致了无法引导进系统。

3  小结

        文章写得很随性,主要是说了“如何配置分区表”这件事,我是如何摸索出来的。希望能给遇到类似问题的朋友带来一定的帮助。欢迎一起探讨学习(我只是小白哈)。


版权声明:本文为Millium原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。