Linux system rootfs switched to the real root file system Detailed source code analysis (attached a working project to manually create a root file example)

1. Introduction

Recently, there is a development board that needs to make a customized Linux operating system. Through this project, I will summarize relevant information, including switching from rootfs to the real root file system, how to make a root file system and related startup configuration. Machine introduction: ARM-based
architecture A53 development board integrates the company's self-developed main control chip, uses uboot to boot, the system is installed on ssd, and the kernel starts from nand flash;

2. Summary of the design process

1. Busybox makes a streamlined file system and completes the ramfs startup;
2. Makes a complete file system through the streamlined file system and copies it to the hard disk;
3. Rootfs switches to the real root file configuration;
4. Startup parameter settings;

2.1 Busybox makes a streamlined file system and completes ramfs startup

1. First download the source code of busybox:
wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
2. Modify the startup configuration;

vim etc/inittab 
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::restart:/sbin/init
vim etc/init.d/rcS
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys

3. Use the busybox small environment to compile and produce Image.gz in the kernel;

make -j4 defconfig
make INSTALL_MOD_PATH=$rootfs  modules_install  //rootfs为busybox小环境
make -j4 vmlinux Image Image.gz "$@"
make dtbs

4. Starting Image.gz in uboot can mount the file system in the memory in the form of ramfs;

2.2 Create a complete file system through a streamlined file system and copy it to the hard disk

1. The complete file system produced this time is to directly cross-download related file systems on other machines to generate an arm-based root file system, and then pack it into the hard disk;

sudo apt-get install debian-archive-keyring
sudo apt-get install binfmt-support qemu qemu-user-static debootstrap 
sudo debootstrap --arch=arm64 --foreign stretch debian_arm64_stretch http://mirrors.ustc.edu.cn/debian/
sudo cp /usr/bin/qemu-aarch64-static debian_arm64_stretch/usr/bin

2. The general process is to first download the environment where the arm platform can be cross-chrooted. This time, the x86 machine is used for operation, and then chroot to the arm environment to download the relevant packages and then exit;

./ch-mount.sh  debian_arm64_stretch/
cat ./ch-mount.sh
#!/bin/bash
    echo "MOUNTING"
    sudo mount -t proc /proc ${
    
    1}proc
    sudo mount -t sysfs /sys ${
    
    1}sys
    sudo mount -o bind /dev ${
    
    1}dev
    sudo mount -o bind /dev/pts ${
    
    1}dev/pts
    sudo chroot ${
    
    1}

3. Download related packages

apt update
apt install sudo
apt install ssh
apt install net-tools
apt install ethtool 
apt install ifupdown
apt install iputils-ping
apt install rsyslog
apt install htop 
apt install vim

Note: If there is no source, manually add it to the etc/apt/sources.list file

4. Package to the hard disk
Mount the boot hard disk to other systems and format it as ext4:

mkfs.ext4 /de/sdb 
fdisk /de/sdb  //创建一个根分区设置100G(根据实际硬盘大小设置,建议在30G以上)
mkfs.ext4  /dev/sdb1 
将制作的根文件系统拷贝到sdb1

My Blog: Partitioning and Packing the Root Filesystem

2.3 switch rootfs to real root file configuration

It can be added directly in /init in the busybox environment, or directly overwrite the original etc/init.d/rcS

vim /init
#!/bin/bash
echo "Hello Linux"
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
export ROOTMNT=/root
export ROFLAG=-r

mount -n -t devtmpfs udev /dev
mount -n -t proc proc /proc
mount -n -t sysfs sysfs /sys
mount -n -t ramfs ramfs /run
udevd --daemon
udevadm trigger --action=add
udevadm settle
for x in $(cat  /proc/cmdline); do
	case $x in 
	root=*)
		ROOT=${
    
    x#root=}
		;;
	ro)
		ROFLAG=-r
		;;
	rw)
		ROFLAG=-w
		;;
	esac
done

mount ${
    
    ROFLAG} ${
    
    ROOT} ${
    
    ROOTMNT}
export ROOTMNT=/a
udevadm control --exit
mount -n --move /dev  /a/dev
mount -n --move /run  ${
    
    ROOTMNT}/run
mount -n --move /proc ${
    
    ROOTMNT}/proc
mount -n --move /sys  ${
    
    ROOTMNT}/sys
switch_root ${
    
    ROOTMNT} /sbin/init


#!/bin/bash
mount -o remount,rw /dev/sda1 /
udevd --daemon
udevadm trigger --action=add
udevadm settle

export HOME=/root
exec /bin/bash -l



mkdir /mnt/sysroot
mount /dev/sda1  /mnt/sysroot
echo "The pid now is $$"  
exec switch_root  /mnt/sysroot /sbin/init



mount -t ext4 /dev/sda1 /mnt/sysroot
mount -n --move /sys /mnt/sysroot/sys
mount --move /proc /mnt/sysroot/proc
mount --move /dev /mnt/sysroot/dev
exec switch_root -c /dev/console /mnt/sysroot /sbin/init
chroot	/mnt/sysroot	/bin/bash

echo "Mount real rootfs to /mnt/..."
mount -t ext4 /dev/sda1 /mnt  #挂载真正的文件系统
#echo "Switch to read rootfs..."
exec switch_root /mnt /sbin/init   #切换到真正的文件系统

sleep 10
mkdir /mnt/lower /mnt/upper /mnt/work /mnt/sysroot
/bin/mount -t squashfs /media/sr0/casper/filesystem.squashfs /mnt/lower -o loop
/bin/mount -t overlay -o lowerdir=/mnt/lower,upperdir=/mnt/upper,workdir=/mnt/work overlay /mnt/sysroot
echo "The pid now is $$"                   //打印当前pid 确保为1
exec switch_root  /mnt/sysroot /sbin/init             //转换

Note: The root partition made this time is in the sda1 partition of the hard disk

2.4 Start parameter setting

1. The information that linux relies on when mounting partitions is stored in the root file system /etc/fstab file

cat /etc/fstab
/dev/sda1 / ext4 defaults 0 1

My blog: /etc/fstab detailed explanation

2. Start parameter setting

setenv bootargs console=ttyS0,115200  root=/dev/sda1  rw  init=/init

My blog: Detailed explanation of uboot startup

3. Hard disk startup verification

After the linux startup is complete, you can pass:

mount |grep sd  
/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro)
//说明此时已经从sda1启动

4. Summary of failure to boot from hard disk

I spent a long time transitioning from ramfs to the root file system of the hard disk during the completion of this project. The main reasons for the analysis are as follows: 1. There is a problem with the init file
code in busybox. The method used later is to directly enter the ramfs started by uboot Generate img directly, then unpack the img manually, and replace the init inside with the init in Image.gz:

mkinitramfs -o test.img 5.10.0
mkdir a
cd a;cpio -idvm <../test.img

My blog: Detailed explanation of mkinitrd command (note that some systems are mkinitramfs)

My blog: cpio format manual unlock command

2. Add init=/init to the startup parameters;
3. Add automount parameters to the hard disk /etc/fstab: /dev/sda1 / ext4 defaults 0 1

The init file in the img generated by mkinitramfs is attached as follows

#!/bin/bash

# Default PATH differs between shells, and is not automatically exported
# by klibc dash.  Make it consistent.
export PATH=/sbin:/usr/sbin:/bin:/usr/bin

[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir -m 0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
mkdir -p /var/lock
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc

case " $(cat /proc/cmdline) " in
*\ quiet\ *)
	quiet=y
	;;
*)
	quiet=n
	echo "Loading, please wait..."
	;;
esac
export quiet

# Note that this only becomes /dev on the real filesystem if udev's scripts
# are used; which they will be, but it's worth pointing out
mount -t devtmpfs -o nosuid,mode=0755 udev /dev
mkdir /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts || true
mount -t tmpfs -o "noexec,nosuid,size=10%,mode=0755" tmpfs /run
mkdir -m 0755 /run/initramfs

# Export the dpkg architecture
export DPKG_ARCH=
. /conf/arch.conf

# Set modprobe env
export MODPROBE_OPTIONS="-qb"

# Export relevant variables
export ROOT=
export ROOTDELAY=
export ROOTFLAGS=
export ROOTFSTYPE=
export IP=
export BOOT=
export BOOTIF=
export UBIMTD=
export break=
export init=/sbin/init
export readonly=y
export rootmnt=/root
export debug=
export panic=
export blacklist=
export resume=
export resume_offset=
export drop_caps=
export fastboot=n
export forcefsck=n
export fsckfix=


# Bring in the main config
. /conf/initramfs.conf
for conf in conf/conf.d/*; do
	[ -f ${conf} ] && . ${conf}
done
. /scripts/functions

# Parse command line options
for x in $(cat /proc/cmdline); do
	case $x in
	init=*)
		init=${x#init=}
		;;
	root=*)
		ROOT=${x#root=}
		if [ -z "${BOOT}" ] && [ "$ROOT" = "/dev/nfs" ]; then
			BOOT=nfs
		fi
                ;;
	rootflags=*)
		ROOTFLAGS="-o ${x#rootflags=}"
		;;
	rootfstype=*)
		ROOTFSTYPE="${x#rootfstype=}"
		;;
	rootdelay=*)
		ROOTDELAY="${x#rootdelay=}"
		case ${ROOTDELAY} in
		*[![:digit:].]*)
			ROOTDELAY=
			;;
		esac
		;;
	nfsroot=*)
		NFSROOT="${x#nfsroot=}"
		;;
	ip=*)
		IP="${x#ip=}"
		;;
	boot=*)
		BOOT=${x#boot=}
		;;
	ubi.mtd=*)
		UBIMTD=${x#ubi.mtd=}
		;;
	resume=*)
		RESUME="${x#resume=}"
		case $RESUME in
	        UUID=*)
			RESUME="/dev/disk/by-uuid/${RESUME#UUID=}"
		esac
		;;
	resume_offset=*)
		resume_offset="${x#resume_offset=}"
		;;
	noresume)
		noresume=y
		;;
	drop_capabilities=*)
		drop_caps="-d ${x#drop_capabilities=}"
		;;
	panic=*)
		panic="${x#panic=}"
		case ${panic} in
		*[![:digit:].]*)
			panic=
			;;
		esac
		;;
	ro)
		readonly=y
		;;
	rw)
		readonly=n
		;;
	debug)
		debug=y
		quiet=n
		if [ -n "${netconsole}" ]; then
			exec >/dev/kmsg 2>&1
		else
			exec >/run/initramfs/initramfs.debug 2>&1
		fi
		set -x
		;;
	debug=*)
		debug=y
		quiet=n
		set -x
		;;
	break=*)
		break=${x#break=}
		;;
	break)
		break=premount
		;;
	blacklist=*)
		blacklist=${x#blacklist=}
		;;
	netconsole=*)
		netconsole=${x#netconsole=}
		[ "x$debug" = "xy" ] && exec >/dev/kmsg 2>&1
		;;
	BOOTIF=*)
		BOOTIF=${x#BOOTIF=}
		;;
	fastboot|fsck.mode=skip)
		fastboot=y
		;;
	forcefsck|fsck.mode=force)
		forcefsck=y
		;;
	fsckfix|fsck.repair=yes)
		fsckfix=y
		;;
	fsck.repair=no)
		fsckfix=n
		;;
	esac
done

# Default to BOOT=local if no boot script defined.
if [ -z "${BOOT}" ]; then
	BOOT=local
fi

if [ -n "${noresume}" ] || [ "$RESUME" = none ]; then
	export noresume=y
	unset resume
else
	resume=${RESUME:-}
fi

maybe_break top

# Don't do log messages here to avoid confusing graphical boots
run_scripts /scripts/init-top

maybe_break modules
[ "$quiet" != "y" ] && log_begin_msg "Loading essential drivers"
[ -n "${netconsole}" ] && modprobe netconsole netconsole="${netconsole}"
load_modules
[ "$quiet" != "y" ] && log_end_msg

if [ "$ROOTDELAY" ]; then
	sleep $ROOTDELAY
fi

maybe_break premount
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-premount"
run_scripts /scripts/init-premount
[ "$quiet" != "y" ] && log_end_msg

maybe_break mount
log_begin_msg "Mounting root file system"
# Always load local and nfs (since these might be needed for /etc or
# /usr, irrespective of the boot script used to mount the rootfs).
. /scripts/local
. /scripts/nfs
. /scripts/${BOOT}
parse_numeric ${ROOT}
maybe_break mountroot
mount_top
mount_premount
mountroot
log_end_msg

if read_fstab_entry /usr; then
	log_begin_msg "Mounting /usr file system"
	mountfs /usr
	log_end_msg
fi

# Mount cleanup
mount_bottom
nfs_bottom
local_bottom

maybe_break bottom
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-bottom"
# We expect udev's init-bottom script to move /dev to ${rootmnt}/dev
run_scripts /scripts/init-bottom
[ "$quiet" != "y" ] && log_end_msg

# Move /run to the root
mount -n -o move /run ${rootmnt}/run

validate_init() {
	run-init -n "${rootmnt}" "${1}"
}

# Check init is really there
if ! validate_init "$init"; then
	echo "Target filesystem doesn't have requested ${init}."
	init=
	for inittest in /sbin/init /etc/init /bin/init /bin/sh; do
		if validate_init "${inittest}"; then
			init="$inittest"
			break
		fi
	done
fi

# No init on rootmount
if ! validate_init "${init}" ; then
	panic "No init found. Try passing init= bootarg."
fi

maybe_break init

# don't leak too much of env - some init(8) don't clear it
# (keep init, rootmnt, drop_caps)
unset debug
unset MODPROBE_OPTIONS
unset DPKG_ARCH
unset ROOTFLAGS
unset ROOTFSTYPE
unset ROOTDELAY
unset ROOT
unset IP
unset BOOT
unset BOOTIF
unset UBIMTD
unset blacklist
unset break
unset noresume
unset panic
unset quiet
unset readonly
unset resume
unset resume_offset
unset fastboot
unset forcefsck
unset fsckfix

# Move virtual filesystems over to the real filesystem
mount -n -o move /sys ${rootmnt}/sys
mount -n -o move /proc ${rootmnt}/proc

# Chain to real filesystem
exec run-init ${drop_caps} ${rootmnt} ${init} "$@" <${rootmnt}/dev/console >${rootmnt}/dev/console 2>&1
echo "Something went badly wrong in the initramfs."
panic "Please file a bug on initramfs-tools."

5. My other blog links

1. Manually partition and package the root file system

2. Detailed explanation of /etc/fstab

3. Detailed explanation of uboot startup

4. Detailed explanation of mkinitrd command (note that some systems are mkinitramfs)

5. Manually unlock the command in cpio format

6. Detailed explanation of grub.cfg startup

6. Other optimization items

1. During the test phase, the kernel Image.gz and the device data dtb are started by tftp. After the project is completed, the files are written to nand to start; 2. The
hard disk is started by UUID, which is suitable for multiple hard disks connected to the machine and cannot be fixed. The situation of the hard disk number;

Guess you like

Origin blog.csdn.net/Luckiers/article/details/125960664