Here I post my init script. It is used in my initrd/initramfs boot images for the following purposes.
- Boot from a live CD / DVD
- Boot from a read-only filesystem image, compressed in SquashFS
- Copy the filesystem image to a RAM disk and run Linux entirely on memory
- Boot Linux from a USB flash drive
- Boot Linux from a local disk partition
- Run a rescue shell without booting Linux
I have yet to implement network booting. Right now it is good enough for my current needs. It takes the following boot parameters.
- root=
Specifies the root partition to boot Linux from. For example, /dev/sda2. - label=
Specifies the label of the device to boot from. For example, label=DEBIAN. - uuid=
Specifies the UUID of the device to boot from. For example, uuid=dcfd6a0a-2a0f-4b3d-8a1a-5e7d642ebfbd - boot=
Can be cdrom, loop, ram, usb or ata. - vmode=
Specifies the screen resolution of the framebuffer video. For example, vmode=640x480 - single Boot into the single-user mode.
- nox
Boot into the console mode in runlevel 2
#!/bin/dash # Define a function to parse kernel command line options. get_opt() { echo $@ | cut -d "=" -f 2- } # Define a function to load drivers. loadmod() { for i in $@ ; do for j in $(grep $i /tmp/pcimodules.txt); do modprobe $j done done } # Define a function to load drivers for USB controllers modusb() { lspci -n | grep "0c03: " && modprobe -a ohci-hcd uhci-hcd ehci-hcd xhci-hcd } # Define a function to guess the partition type. gpart() { for i in $(blkid | grep $1); do case $i in *\=*) eval $i ;; *) true ;; esac done } # Define a function for mounting the root partition. mountr() { if [ $uuid ]; then [ $# = 1 ] && mount -r -U $uuid $1 || mount -r -U $uuid /mnt elif [ $label ]; then [ $# = 1 ] && mount -r -L $label $1 || mount -r -L $label /mnt else gpart $1 case $TYPE in ext*) e2fsck -p $1 [ $# = 2 ] && mount $1 $2 || mount $1 /mnt ;; jfs) jfs_fsck $1 if [ $# = 2 ]; then mount -t jfs -o ro,iocharset=utf8 $1 $2 else mount -t jfs -o ro,iocharset=utf8 $1 /mnt fi ;; vfat) if [ $# = 2 ]; then mount -t vfat -o ro,gid=100,dmask=2,fmask=113 $1 $2 else mount -t vfat -o ro,gid=100,dmask=2,fmask=113 $1 /mnt fi ;; *) [ $# = 2 ] && mount -r $1 $2 || mount -r $1 /mnt ;; esac fi } # Define a function for creating a union filesystem union() { mount -t tmpfs none /opt/tmp modinfo unionfs > /dev/null 2>&1 && mount -t unionfs -o dirs=/opt/tmp=rw:/opt=ro none /mnt || ( mkdir /opt/tmp/.change modprobe fuse unionfs-fuse -o allow_other,use_ino,suid,dev,nonempty,kernel_cache \ -o cow,chroot=/opt,max_files=32768 /tmp/.change=RW:/=RO /mnt ) mount --rbind /opt /mnt/opt } # Mount proc and sysfs. mount -t proc none /proc mount -t sysfs none /sys # Populate /dev (Needs kernel >= 2.6.32) mount -t devtmpfs none /dev mkdir -m 755 /dev/pts mount -t devpts -o gid=5,mode=620 none /dev/pts # Find modules required to use available PCI hardware mount -t tmpfs none /tmp for f in $(lspci -n | awk '{print toupper($3)}' | sed -e 's/:/d0000/g') do grep $f /lib/modules/$(uname -r)/modules.alias \ | awk '{print $3}' >> /tmp/pcimodules.txt done lspci -n | awk '$2 == "0c00:" {print "firewire-ohci"}' >> /tmp/modules.txt # Find the root=, label=, uuid= and boot= values on kernel command line. for i in $(cat /proc/cmdline); do case $i in root\=*) root=$(get_opt $i) case $root in /dev/cdr* | /dev/dvd* | /dev/sr* | /dev/scd*) boot=cdrom ;; 0x200) root=/dev/fd0 ;; label\=* | LABEL\=* | uuid\=* | UUID\=* ) eval $(echo $root | tr 'A-Z' 'a-z') ;; esac ;; label\=* | uuid\=* | boot\=* | vmode\=* ) eval $i ;; single) RUNLEVEL=single ;; nox) RUNLEVEL=2 ;; esac done # Set default values [ "$boot" ] || boot=ata [ "$uuid$label$root" ] || root=/dev/sda6 # Activate framebuffer display devices. grep -q i915 /tmp/pcimodules.txt || if [ $vmode -a $boot = cdrom ]; then modprobe uvesafb scroll=ywrap mode_option=$vmode-16 elif [ $vmode ]; then for i in $(grep fb /tmp/pcimodules.txt); do case $i in atyfb) modprobe $i mode=$vmode-16 ;; nvidiafb | rivafb) modprobe nvidiafb mode_option=$vmode bpp=16 hwcur=1 ;; radeonfb | savagefb) modprobe $i mode_option=$vmode-16 ;; sisfb) modprobe $i mode=$vmodex16 mem=12288 font=SUN12x22 ;; viafb | vt8623fb) modprobe viafb viafb_mode=$vmode viafb_bpp=16 ;; *) modprobe $i ;; esac done [ -c /dev/fb0 ] || modprobe uvesafb scroll=ywrap mode_option=$vmode-16 fi case $boot in cdrom) # Boot Linux from a live CD. loadmod ata_ ahci pdc_adma modusb modprobe usb-storage && modprobe sr_mod && sleep 7 modprobe isofs mount -t iso9660 /dev/sr0 /media [ -d /media/isolinux -o -d /media/boot/isolinux ] || mount -t iso9660 /dev/sr1 /media if [ -f /media/*.[Ss][Qq]* ]; then SQF=$(ls -t /media/*.[Ss][Qq]* | head -n 1) if [ $root = /dev/ram ]; then echo "Please wait until the RAM disk is ready." dd if=$SQF of=/dev/ram1 bs=2048 && mount -t squashfs /dev/ram1 /opt else modprobe loop mount -t squashfs -o loop $SQF /opt fi else mount --move /media /opt fi union ;; loop) # Boot Linux from an image file. loadmod ata_ ahci pdc_adma modusb modprobe usb-storage && modprobe sd_mod && sleep 7 [ "$uuid$label" ] && mountr /media || mountr $root /media modprobe loop if [ -f /media/*.[Ss][Qq]* ]; then SQF=$(ls -t /media/*.[Ss][Qq]* | head -n 1) mount -t squashfs -o loop $SQF /opt elif [ -f /media/*.[Ii][Ss][Oo] ]; then ISO=$(ls -t /media/*.[Ii][Ss][Oo] | head -n 1) modprobe isofs mount -t iso9660 -o loop $ISO /opt fi union ;; ram) # Boot Linux from ramdisk. loadmod ata_ ahci pdc_adma modusb modprobe usb-storage && modprobe sd_mod && sleep 7 [ "$uuid$label" ] && mountr /media || mountr $root /media echo "Please wait until the RAM disk is ready." if [ -f /media/*.[Ss][Qq]* ]; then SQF=$(ls -t /media/*.[Ss][Qq]* | head -n 1) dd if=$SQF of=/dev/ram1 && mount -t squashfs /dev/ram1 /opt elif [ -f /media/*.[Ii][Ss][Oo] ]; then ISO=$(ls -t /media/*.[Ii][Ss][Oo] | head -n 1) dd if=$ISO of=/dev/ram1 bs=2048 && modprobe isofs mount -t iso9660 /dev/ram1 /opt fi union ;; usb*) # Boot Linux from a USB drive. modusb modprobe usb-storage && modprobe sd_mod && sleep 7 [ "$uuid$label" ] && mountr || mountr $root ;; ata*) loadmod ata_ ahci pdc_adma && modprobe sd_mod && [ "$uuid$label" ] && mountr || mountr $root ;; esac # Make sure that init exists and is executable. if [ -x /mnt/sbin/init ]; then mount --move /dev /mnt/dev mount --move /proc /mnt/proc mount --move /sys /mnt/sys umount /tmp # Start init from the root filesystem. cd /mnt [ -f /media/updates.zip ] && unzip -o /media/updates.zip case $boot in cdrom) [ $root = /dev/ram ] && umount /media [ $RUNLEVEL ] || RUNLEVEL=3 ;; loop | ram) umount /media [ $RUNLEVEL ] || RUNLEVEL=3 ;; *) [ $RUNLEVEL ] || RUNLEVEL=5 ;; esac [ -d initrd ] && pivot_root . initrd exec switch_root . /sbin/init $RUNLEVELdev/console 2>&1 fi # Start a shell as a last resort. echo "Error booting from the root filesystem. Starting a shell." exec /bin/dash
The following are examples of boot parameters that can be used with my init script.
- Boot Linux from the local hard drive partition /dev/sda8
boot=ata root=/dev/sda8
- Boot Linux from the latest squashfs file (*.sq*) on /dev/sda1
boot=loop root=/dev/sda1
- Boot Linux from the CD-ROM with 1024x768 video resolution
boot=cdrom vmode=1024x768
- Copy the squashfs image from CD-ROM into memory and run Linux on memory
boot=cdrom root=/dev/ram ramdisk_size=573440 vmode=800x600
- Boot Linux from the second partition of a USB drive
boot=usb root=/dev/sda2
Thank you. For me kernel 3.6.1 enumerates usb devices first so I've finally had to admit defeat and use a initrd. Your script got me up and going quickly.
ReplyDeletePerhaps root=uuid=... is a bit more standard.
Thanks.
You're welcome. The script was updated to support root=uuid= and root=label= ...
DeleteKJY