小麦克 发表于 2024-8-11 07:18

【米尔 NXP i.MX93 开发板评测】RAMDISK实践

# RAMDISK实践

> 编译开发板自带的内核源码,制作一个`RAMDISK`,放到SD卡中加载测试内核;由于UBOOT功能有限,可以使用ramdisk添加更高级的维护功能,比如设备升级与工厂生产刷机。后面我们实现升级功能时会用到。

## 制作RAMDISK

使用`busybox`生成一个精简`initramdisk`

### 内核编译

参考米尔文档 《MYD-LMX9X_Linux软件开发指南.pdf》

```bash
# 配置交叉编译器
source /opt/fsl-imx-xwayland/6.1-mickledore/environment-setuparmv8a-poky-linux

cd MYD-LMX9X-Linux-L6.1.55-V1.0.0
./build-linux-zh.sh

# 选择编译线程数,等待一会儿编译成功后会生成output目录
cd output
output$ ll -h
总计 65M
drwxrwxr-x 2 bruce bruce 4.0K Aug 11 06:42 ./
drwxrwxr-x 4 bruce bruce 4.0K Aug 11 06:31 ../
-rw-rw-r-- 1 bruce bruce34M Aug 11 06:42 Image
-rw-rw-r-- 1 bruce bruce31M Aug 11 06:42 modules.tar.gz
-rw-rw-r-- 1 bruce bruce45K Aug 11 06:42 myir-imx93-11x11.dtb
-rw-rw-r-- 1 bruce bruce47K Aug 11 06:42 myir-imx93-11x11-lvds-10-1.dtb
-rw-rw-r-- 1 bruce bruce46K Aug 11 06:42 myir-imx93-11x11-lvds.dtb
-rw-rw-r-- 1 bruce bruce46K Aug 11 06:42 myir-imx93-11x11-RGB.dtb
-rw-rw-r-- 1 bruce bruce45K Aug 11 06:42 myir-imx93-11x11-root.dtb
```

重新配置内核,支持`RAM block device`



```bash
# 进入内核源代码目录
cd myir-imx-linux
# 配置
make menuconfig

# 将RAM block驱动编译进内核
Device Drivers--->
        Block devices--->
                <*>   RAM block device support
                        (1)   Default number of RAM disks
                        (65536) Default RAM disk size (kbytes)

make

# 生成我们需要的 arch/arm64/boot/Image
```

**注意:**这里不能使用`./build-linux-zh.sh`,脚本编译时会再次使用DEFCONFIG生成配置文件`.config`,会将我们设置好的配置清除掉。如果想用,可以重新拷贝一份deconfig,配置好`RAM DISK`,然后替代这里的 `DEFCONFIG="imx_v8_defconfig"`

```shell
DEFCONFIG="imx_v8_defconfig"

compile_linux(){
    echo "开始配置内核"
    cd ${LOACL_PATH}/${LINUX_NAME}
    make ${DEFCONFIG} > /dev/null 2>&1
    echo "开始编译内核"
    LDFLAGS="" CC="$CC" make -j ${THREAD_NUM}> /dev/null 2>&1 &
    make_pid=$!
    bar_test ${make_pid}
    echo "编译完成"
}
```


### update.sh

编写刷机脚本 `update.sh`,需要添加执行权限

```bash
#!/bin/sh

# eMMC device node
node=/dev/mmcblk0

# size in MB
BOOT_ROM_SIZE=8
ROOT_FS_SIZE=5120
PARAM_FS_SIZE=128
APP_FS_START=5416

mount -o remount,rw /

# wait for the SD/MMC device node ready
while [ ! -e ${node} ]
do
        sleep 1
        echo "wait for ${node} appear"
done

# TODO: check eMMC partitions
EMMC_PARTITIONS=("/dev/mmcblk0p1" "/dev/mmcblk0p2" "/dev/mmcblk0p3")

for PARTITION in "${EMMC_PARTITIONS[@]}"; do
        if [ -e "$PARTITION" ]; then
                echo "$PARTITION exist."
        else
                echo "$PARTITION not exist."
        fi
done

# call sfdisk to create partition table
# destroy the partition table
dd if=/dev/zero of=${node} bs=1024 count=1

# p1 8M    ~ 128M   FAT(120MB)
# p2 128M~ 5248MROOTFS(5GB)
# p3 5248M ~ 5376   PARAM(128MB)
# p4 5376~ END    APP(left)
echo ">>> sfdisk ${node}"
sfdisk --force ${node} << EOF
${BOOT_ROM_SIZE}M,120M,0c
,${ROOT_FS_SIZE}M,83
,${PARAM_FS_SIZE}M,83
${APP_FS_START}M,,83
EOF

if [ -b /dev/mmcblk1p1 ]
then
        echo ">>> mount SD /dev/mmcblk1p1 -> /mnt"
        mount /dev/mmcblk1p1 /mnt
elif [ -b /dev/sda1 ]
then
        echo ">>> mount SD /dev/sda1 -> /mnt"
        mount /dev/sda1 /mnt
else
        echo "ERROR: The /dev/sda1 of the sd card was not found."
        exit 1
fi

# burn uboot
if [ -f /mnt/flash/flash.bin ]
then
        echo ">>> burn flash.bin -> /dev/mmcblk0boot0"
        echo 0 > /sys/block/mmcblk0boot0/force_ro
        dd if=/mnt/flash/flash.bin of=/dev/mmcblk0boot0 conv=fsync
        echo 1 > /sys/block/mmcblk0boot0/force_ro

        mmc bootpart enable 1 1 /dev/mmcblk0
fi

while [ ! -e /dev/mmcblk0p1 ]
do
        sleep 1
        echo "waiting... /dev/mmcblk0p1"
done

# burn dtb and Image
if [ -f /mnt/flash/Image ]
then
        echo ">>> copy dtb and Image -> /dev/mmcblk0p1"
        mkfs.vfat /dev/mmcblk0p1
        mkdir -p /tmp/kernel
        mount -t vfat /dev/mmcblk0p1 /tmp/kernel
        cp /mnt/flash/Image /tmp/kernel
        cp /mnt/flash/imx93-11x11-atk.dtb /tmp/kernel
        cp /mnt/flash/ramdisk.img.gz /tmp/kernel
        sync
        umount /tmp/kernel
fi

# burn rootfs
if [ -f /mnt/flash/rootfs.tar ]
then
        echo ">>> burn rootfs -> /dev/mmcblk0p2"
        # mkfs.ext4 -F -E nodiscard /dev/mmcblk0p2
        mkfs.ext4 -F /dev/mmcblk0p2
        mkdir -p /tmp/rootfs
        mount -t ext4 /dev/mmcblk0p2 /tmp/rootfs
        tar -xf /mnt/flash/rootfs.tar -C /tmp/rootfs
        sync
        umount /tmp/rootfs
fi

# parameter: 128MB
mkfs.ext4 -F -E nodiscard /dev/mmcblk0p3
# APP: using left space
mkfs.ext4 -F -E nodiscard /dev/mmcblk0p4

sync
sleep 1

echo ">>>>>>>>>>> DONE <<<<<<<<<<<"
```

### 编译busybox

```bash
# 下载 busybox 源码
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2

# 解压
tar -xf busybox-1.36.1.tar.bz2

# 进入 busybox 目录
cd busybox-1.36.1

# 使用默认配置
make menuconfig

# 配置静态编译、交叉编译器前缀、安装路径;
Settings--->
        [*] Build static binary (no shared libs)
        (aarch64-none-linux-gnu-) Cross compiler prefix
        (../ramdisk) Destination path for 'make install'

# 在配置了交叉编译器的前提下
make -j8

# 安装 busybox 文件
make install

# 进入安装目录,创建一些必须的配置文件
cd ../ramdisk/
mkdir -p mnt tmp var usr sys proc etc lib dev bin sbin root home
mkdir -p usr/lib lib/modules

# 创建设备节点,一个 console,一个 null
cd dev/
sudo mknod -m 666 console c 5 1
sudo mknod -m 666 null c 1 3
cd ../

# 复制 busybox 中的 examples/bootfloppy/etc 的文件
cp ../busybox-1.36.1/examples/bootfloppy/etc ./ -a

# 复制之前静态编译的 mke2fs 到 bin 目录,并重命名为 mkfs.ext4
cp ../e2fsprogs-1.47.0/misc/mke2fs ./bin/mkfs.ext4

# 复制之前静态编译的 resize2fs 到 bin 目录
cp ../e2fsprogs-1.47.0/resize/resize2fs ./bin/resize2fs

# 复制之前静态编译的 sfdisk.static 到 bin 目录,并重命名为 sfdisk
cp ../util-linux-2.40/sfdisk.static ./bin/sfdisk

# 复制之前静态编译的 mmc 到 bin 目录
cp ../mmc-utils/mmc ./bin/mmc

# 对 etc/fstab 文件添加一行,挂载 sysfs 文件系统,修改后如下
$ cat etc/fstab
proc                /proc        proc        defaults        0        0
sysfs                /sys        sysfs        defaults        0        0
```

/etc/init.d/rcS

```shell
#!/bin/sh

/bin/mount -a
/bin/hostname imx93
ifconfig eth0 up

sleep 1
udhcpc &
sleep 2

for initscript in /etc/init.d/S*
do
        if [ -x $initscript ] ;
        then
                echo ": $initscript"
                $initscript
        fi
done
```

/etc/profile

```shell
PATH=/bin:/sbin:/usr/bin:/usr/sbin
PS1='/$ '
HOSTNAME='/bin/hostname'
export PATH HOSTNAME PS1
alias l.='ls -d .* --color=tty'
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
```

### 打包

将`update.sh`和其他需要的程序可以可以一起放到文件系统中

```bash
# 在 etc/init.d/rcS 中添加一行 update.sh 脚本的调用
$cat etc/init.d/rcS
#! /bin/sh

/bin/mount -a
/update.sh &

# 修改文件的用户与用户组为 root
sudo chown root:root -R ramdisk/*

# 如果genext2fs没安装,先安装
apt-get install genext2fs

# 制作一个8MB大小的ramdisk,需要小于在内核中配置的Default RAM disk size
genext2fs -b 8192 -d ramdisk ramdisk.img

# 压缩成ramdisk.img.gz
gzip ramdisk.img
```

### 加载测试

```bash
fatload mmc 1:1 0x80400000 Image
fatload mmc 1:1 0x83000000 myir-imx93-11x11.dtb
fatload mmc 1:1 0x84000000 ramdisk.img.gz
setenv bootargs 'console=ttyLP0,115200 earlycon initrd=0x84000000,0x800000 root=/dev/ram0 rw rootfstype=ext4 rootwait'
booti 0x80400000 - 0x83000000
```

只需要两秒多就加载成功了




### 网络配置

开发板启动通过DHCP获取IP

创建文件 `/etc/network/interfaces`

```shell
auto eth0
```



### udhcpc测试

编译busybox时已经添加了`udhcpc`

在ramdisk中添加 `/usr/share/udhcpc/default.script`

```shell
#!/bin/sh
# udhcpc script edited by Tim Riker <Tim@Rikers.org>

RESOLV_CONF="/etc/resolv.conf"

[ -n "$1" ] || { echo "Error: should be called from udhcpc"; exit 1; }

NETMASK=""
if command -v ip >/dev/null; then
      [ -n "$subnet" ] && NETMASK="/$subnet"
else
      [ -n "$subnet" ] && NETMASK="netmask $subnet"
fi
BROADCAST="broadcast +"
[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"

case "$1" in
      deconfig)
                echo "Clearing IP addresses on $interface, upping it"
                if command -v ip >/dev/null; then
                        ip -4 addr flush dev $interface
                        ip link set dev $interface up
                else
                        ifconfig $interface 0.0.0.0
                fi
                ;;

      renew|bound)
                echo "Setting IP address $ip on $interface"
                if command -v ip >/dev/null; then
                        ip addr add $ip$NETMASK $BROADCAST dev $interface
                else
                        ifconfig $interface $ip $NETMASK $BROADCAST
                fi

                if [ -n "$router" ] ; then
                        echo "Deleting routers"
                        while route del default gw 0.0.0.0 dev $interface ; do
                              :
                        done

                        metric=0
                        for i in $router ; do
                              echo "Adding router $i"
                              if [ "$subnet" = "255.255.255.255" ]; then
      # special case for /32 subnets:
      # /32 instructs kernel to always use routing for all outgoing packets
      # (they can never be sent to local subnet - there is no local subnet for /32).
      # Used in datacenters, avoids the need for private ip-addresses between two hops.
                                        ip route add $i dev $interface
                              fi
                              route add default gw $i dev $interface metric $((metric++))
                        done
                fi

                # If the file is a symlink somewhere (like /etc/resolv.conf
                # pointing to /run/resolv.conf), make sure things work.
                if test -L "$RESOLV_CONF"; then
                        # If it's a dangling symlink, try to create the target.
                        test -e "$RESOLV_CONF" || touch "$RESOLV_CONF"
                fi
                realconf=$(readlink -f "$RESOLV_CONF" 2>/dev/null || echo "$RESOLV_CONF")
                echo "Recreating $realconf"
                tmpfile="$realconf-$$"
                > "$tmpfile"
                [ -n "$domain" ] && echo "search $domain" >> "$tmpfile"
                for i in $dns ; do
                        echo " Adding DNS server $i"
                        echo "nameserver $i" >> "$tmpfile"
                done
                mv "$tmpfile" "$realconf"
                ;;
esac

exit 0
```

**注意:** 这个脚本需要添加执行权限,否则获取不到IP地址

```bash
chmod +x default.script
```

运行测试,成功拿到IP地址

```bash
/usr/share/udhcpc # chmod +x default.script
/usr/share/udhcpc # ls
default.script
/usr/share/udhcpc # udhcpc -v
udhcpc: started, v1.36.1
udhcpc: executing /usr/share/udhcpc/default.script deconfig
Clearing IP addresses on eth0, upping it
udhcpc: entering listen mode: raw
udhcpc: broadcasting discover
udhcpc: waiting 3 seconds
udhcpc: received offer of 172.20.10.10
udhcpc: broadcasting select for 172.20.10.10, server 172.20.10.1
udhcpc: waiting 3 seconds
udhcpc: entering listen mode: none
udhcpc: lease of 172.20.10.10 obtained from 172.20.10.1, lease time 86400
udhcpc: executing /usr/share/udhcpc/default.script bound
Setting IP address 172.20.10.10 on eth0
Deleting routers
route: SIOCDELRT: No such process
Adding router 172.20.10.1
Recreating /etc/resolv.conf
Adding DNS server 172.20.10.1
```



页: [1]
查看完整版本: 【米尔 NXP i.MX93 开发板评测】RAMDISK实践