Skip to content

Instantly share code, notes, and snippets.

@HengYeDev
Created February 16, 2022 23:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HengYeDev/3beb4c607c4bf8ca8fc71c0b3d580adc to your computer and use it in GitHub Desktop.
Save HengYeDev/3beb4c607c4bf8ca8fc71c0b3d580adc to your computer and use it in GitHub Desktop.
#!/bin/sh
#
# Hybris adaptation bootstrapping initramfs init script.
#
# Copyright (c) 2014 Jolla Oy
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 2 as published by the
# Free Software Foundation.
#
# Authors:
# - Tom Swindell <t.swindell@rubyx.co.uk>
# - David Greaves <david@dgreaves.com>
#
# This init script runs in early boot in initrd and does a switch_root
# to the real rootfs. It can also be copied into rootfs and provide
# monitoring of the boot process there too.
# Be careful - you can't run any external commands until busybox has installed.
# General logging
set -x
exec > /init.log 2>&1
echo "Running Mer Boat Loader"
BOOTLOGO=%BOOTLOGO%
ALWAYSDEBUG=%ALWAYSDEBUG%
DATA_PARTITION=%DATA_PART%
DEFAULT_OS=%DEFAULT_OS%
set_welcome_msg(){
cat <<EOF > /etc/issue.net
Welcome to the Mer/SailfishOS Boat loader debug init system.
Log so far is in /init.log
To make post-switch_root halt before starting systemd, perform:
EOF
if [ "$DONE_SWITCH" = "no" ]; then
cat <<EOF >> /etc/issue.net
touch /target/init_enter_debug2
EOF
else
cat <<EOF >> /etc/issue.net
touch /init_enter_debug2
EOF
fi
cat <<EOF >> /etc/issue.net
(When run post-switch_root, telnet is on port 2323, not 23)
EOF
HALT_BOOT="${1:-y}"
if [ "$HALT_BOOT" = "y" ]; then
cat <<EOF >> /etc/issue.net
You may inject commands into init shell process (PID 1):
To see output of commands as they're injected:
tail -f /init.log &
To run a command:
echo "ls -l /" >/init-ctl/stdin
(Be careful if you experiment with exec as you need to terminate
daemons and disable busybox hotplug handling)
To allow init to continue:
echo "continue" >/init-ctl/stdin
EOF
fi
if [ "$DONE_SWITCH" = "no" ]; then
cat <<EOF >> /etc/issue.net
In order to work safely with the device's mmc you should
echo "umount_stowaways" >/init-ctl/stdin
Then you can mount and modify exported mass storage on host. When done
echo "mount_stowaways" >/init-ctl/stdin
EOF
fi
}
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
# Default setting is rndis - add mass_storage for a debug boot
# enable using usb_setup
USB_FUNCTIONS=rndis
ANDROID_USB=/sys/class/android_usb/android0
GADGET_DIR=/config/usb_gadget
LOCAL_IP=192.168.2.15
DONE_SWITCH=no
# Are we running in real rootfs
if [ "$0" = "/init-debug" ]; then
DONE_SWITCH=yes
fi
# Get options from kernel command line
get_opt() {
for param in $(cat /proc/cmdline); do
echo "$param" | grep "^$1=*" | cut -d'=' -f2
done
}
# Minimal mounts for initrd or pre-init debug session
do_mount_devprocsys()
{
echo "########################## mounting devprocsys"
mkdir /dev
mount -t devtmpfs devtmpfs /dev
# telnetd needs /dev/pts/ entries
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mkdir /proc
mkdir /sys
mount -t sysfs sysfs /sys
mount -t proc proc /proc
mkdir /config
mount -t configfs none /config
}
do_hotplug_scan()
{
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
# There is no way to know when all hotplug events have been processed :(
sleep 2
}
bootsplash() {
if [ x$BOOTLOGO = x1 ]; then
zcat /bootsplash.gz > /dev/fb0
fi
}
mount_stowaways() {
echo "########################## mounting stowaways"
if [ ! -z $DATA_PARTITION ]; then
data_subdir="$(get_opt data_subdir)"
mkdir /data
mkdir /target
mount $DATA_PARTITION /data
mount --bind /data/${data_subdir}/.stowaways/${DEFAULT_OS} /target
mkdir /target/data # in new fs
mount --bind /data/${data_subdir} /target/data
else
echo "Failed to mount /target, device node '$DATA_PARTITION' not found!" >> /diagnosis.log
fi
mount
}
umount_stowaways() {
if [ ! -z $DATA_PARTITION ]; then
umount /target/data
umount /target
umount /data
fi
}
# Sugar for accessing usb config
write() {
echo -n "$2" > "$1"
}
inject_loop() {
INJ_DIR=/init-ctl
INJ_STDIN=$INJ_DIR/stdin
mkdir $INJ_DIR
mkfifo $INJ_STDIN
echo "This entire directory is for debugging init - it can safely be removed" > $INJ_DIR/README
echo "########################## Beginning inject loop"
while : ; do
while read IN; do
if [ "$IN" = "continue" ]; then break 2;fi
$IN
done <$INJ_STDIN
done
rm -rf $INJ_DIR # Clean up if we exited nicely
echo "########################## inject loop done"
}
# This sets up the USB with whatever USB_FUNCTIONS are set to via configfs
usb_setup_configfs() {
G_USB_ISERIAL=$GADGET_DIR/g1/strings/0x409/serialnumber
mkdir $GADGET_DIR/g1
write $GADGET_DIR/g1/idVendor "0x18D1"
write $GADGET_DIR/g1/idProduct "0xD001"
mkdir $GADGET_DIR/g1/strings/0x409
write $GADGET_DIR/g1/strings/0x409/serialnumber "$1"
write $GADGET_DIR/g1/strings/0x409/manufacturer "Mer Boat Loader"
write $GADGET_DIR/g1/strings/0x409/product "$CUSTOMPRODUCT"
sync
sleep 5
if echo $USB_FUNCTIONS | grep -q "rndis"; then
mkdir $GADGET_DIR/g1/functions/rndis.usb0
mkdir $GADGET_DIR/g1/functions/rndis_bam.rndis
fi
echo $USB_FUNCTIONS | grep -q "mass_storage" && mkdir $GADGET_DIR/g1/functions/storage.0
mkdir $GADGET_DIR/g1/configs/c.1
mkdir $GADGET_DIR/g1/configs/c.1/strings/0x409
write /config/usb_gadget/g1/configs/c.1/strings/0x409/configuration "Conf 1"
# write $GADGET_DIR/g1/configs/c.1/strings/0x409/configuration "$USB_FUNCTIONS"
if echo $USB_FUNCTIONS | grep -q "rndis"; then
ln -s $GADGET_DIR/g1/functions/rndis.usb0 $GADGET_DIR/g1/configs/c.1
ln -s $GADGET_DIR/g1/functions/rndis_bam.rndis $GADGET_DIR/g1/configs/c.1
fi
echo $USB_FUNCTIONS | grep -q "mass_storage" && ln -s $GADGET_DIR/g1/functions/storage.0 $GADGET_DIR/g1/configs/c.1
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/functions $USB_FUNCTIONS
echo "$(ls /sys/class/udc)" > $GADGET_DIR/g1/UDC
write /sys/class/android_usb/android0/enable 1
}
# This sets up the USB with whatever USB_FUNCTIONS are set to via android_usb
usb_setup_android_usb() {
G_USB_ISERIAL=$ANDROID_USB/iSerial
write $ANDROID_USB/enable 0
write $ANDROID_USB/functions ""
write $ANDROID_USB/enable 1
usleep 500000 # 0.5 delay to attempt to remove rndis function
write $ANDROID_USB/enable 0
write $ANDROID_USB/idVendor 18D1
write $ANDROID_USB/idProduct D001
write $ANDROID_USB/iManufacturer "Mer Boat Loader"
write $ANDROID_USB/iProduct "$CUSTOMPRODUCT"
write $ANDROID_USB/iSerial "$1"
write $ANDROID_USB/functions $USB_FUNCTIONS
write $ANDROID_USB/enable 1
}
# This determines which USB setup method is going to be used
usb_setup() {
if [ -d $ANDROID_USB ]; then
usb_setup_android_usb $1
elif [ -d $GADGET_DIR ]; then
usb_setup_configfs $1
fi
}
# This lets us communicate errors to host (if it needs disable/enable then that's a problem)
usb_info() {
# make sure USB is settled
echo "########################## usb_info: $1"
sleep 1
write $G_USB_ISERIAL "$1"
}
run_debug_session() {
CUSTOMPRODUCT=$1
echo "########################## Debug session : $1"
usb_setup "Mer Debug setting up (DONE_SWITCH=$DONE_SWITCH)"
USB_IFACE=notfound
/sbin/ifconfig rndis0 $LOCAL_IP && USB_IFACE=rndis0
if [ x$USB_IFACE = xnotfound ]; then
/sbin/ifconfig usb0 $LOCAL_IP && USB_IFACE=usb0
fi
# Report for the logs
/sbin/ifconfig -a
# Unable to set up USB interface? Reboot.
if [ x$USB_IFACE = xnotfound ]; then
usb_info "Mer Debug: ERROR: could not setup USB as usb0 or rndis0"
dmesg
sleep 60 # plenty long enough to check usb on host
reboot -f
fi
# Create /etc/udhcpd.conf file.
echo "start 192.168.2.20" > /etc/udhcpd.conf
echo "end 192.168.2.90" >> /etc/udhcpd.conf
echo "lease_file /var/udhcpd.leases" >> /etc/udhcpd.conf
echo "interface $USB_IFACE" >> /etc/udhcpd.conf
echo "option subnet 255.255.255.0" >> /etc/udhcpd.conf
# Be explicit about busybox so this works in a rootfs too
echo "########################## starting dhcpd"
$EXPLICIT_BUSYBOX udhcpd
HALT_BOOT="${2:-y}"
set_welcome_msg $HALT_BOOT
if [ -z $DISABLE_TELNET ]; then
# Non-blocking telnetd
echo "########################## starting telnetd"
# We run telnetd on different ports pre/post-switch_root This
# avoids problems with an unterminated pre-switch_root telnetd
# hogging the port
$EXPLICIT_BUSYBOX telnetd -b ${LOCAL_IP}:${TELNET_DEBUG_PORT} -l /bin/sh
# For some reason this does not work in rootfs
usb_info "Mer Debug telnet on port $TELNET_DEBUG_PORT on $USB_IFACE $LOCAL_IP - also running udhcpd"
fi
if [ "$HALT_BOOT" = "y" ]; then
# Some logging output
ps -wlT
ps -ef
netstat -lnp
cat /proc/mounts
sync
# Run command injection loop = can be exited via 'continue'
inject_loop
fi
}
# writes to /diagnosis.log if there's a problem
check_kernel_config() {
echo "Checking kernel config"
if [ ! -e /proc/config.gz ]; then
echo "No /proc/config.gz. Enable CONFIG_IKCONFIG and CONFIG_IKCONFIG_PROC" >> /diagnosis.log
else
# Must be =y
for x in CONFIG_CGROUPS CONFIG_AUTOFS4_FS CONFIG_DEVTMPFS_MOUNT CONFIG_DEVTMPFS CONFIG_UNIX CONFIG_INOTIFY_USER CONFIG_SYSVIPC CONFIG_NET CONFIG_PROC_FS CONFIG_SIGNALFD CONFIG_SYSFS CONFIG_TMPFS_POSIX_ACL CONFIG_VT; do
zcat /proc/config.gz | grep -E "^$x=y\$" || echo "$x=y not found in /proc/config.gz" >> /diagnosis.log
done
# Must not be =y
for x in CONFIG_DUMMY CONFIG_SYSFS_DEPRECATED; do
zcat /proc/config.gz | grep -E "^$x=y\$" && echo "$x=y found in /proc/config.gz, must be disabled" >> /diagnosis.log
done
fi
}
# Now either initrd or rootfs sequence
if [ "$DONE_SWITCH" = "no" ]; then
EXPLICIT_BUSYBOX=""
TELNET_DEBUG_PORT=23
/bin/busybox --install -s
date
do_mount_devprocsys
do_hotplug_scan
# Support /dev/block/mmcXXX only in initrd phase
ln -s . /dev/block
ln -s /proc/mounts /etc/mtab
check_kernel_config
bootsplash
mount_stowaways
# No target debug unless we debug here too (for now)
DBG_REASON=""
[ -e /diagnosis.log ] && DBG_REASON="Refusing to boot. See /diagnosis.log (in initrd only)"
[ "$(get_opt bootmode)" = "debug" ] && DBG_REASON="bootmode=debug on kernel command line"
[ x$ALWAYSDEBUG = x1 ] && DBG_REASON="Always debug: rndis + mass_storage"
[ -f /target/init_enter_debug ] && DBG_REASON="/init_enter_debug exists"
[ -f /target/init_disable_telnet ] && DISABLE_TELNET="y"
if ! [ "$DBG_REASON" = "" ] ; then
# During debug we export mmc too (some variations in location here)
lun=/sys/class/android_usb/f_mass_storage/lun/file
if [ -f $lun ]; then echo /dev/mmcblk0 > $lun; fi
lun=/sys/class/android_usb/f_mass_storage/lun0/file
if [ -f $lun ]; then echo /dev/mmcblk0 > $lun; fi
USB_FUNCTIONS=rndis,mass_storage
run_debug_session $DBG_REASON
# Tidy up before we switch_root (rootfs init-debug leaves these running during bootup)
killall telnetd
killall udhcpd
USB_FUNCTIONS=rndis
usb_setup "Mer Debug: done debug, disabling storage"
fi
# Disable mdev hotplug now - let udev handle it in main boot
echo "" > /proc/sys/kernel/hotplug
if [ -f "/target/init-debug" ]; then
exec switch_root /target /init-debug &> /target/init-debug-stderrout
else
# Prefer /sbin/preinit over /sbin/init
[ -x /target/sbin/preinit ] && INIT=/sbin/preinit || INIT=/sbin/init
if [ -x "/target$INIT" ]; then
exec switch_root /target $INIT --log-level=debug --log-target=kmsg &> /target/init-stderrout
fi
fi
run_debug_session "Failed to boot init in real rootfs"
else
# We're in the real rootfs running as init-debug
EXPLICIT_BUSYBOX="/bin/busybox-static"
TELNET_DEBUG_PORT=2323
do_mount_devprocsys
HALT_BOOT="n"
[ -f /init_enter_debug2 ] && HALT_BOOT="y"
[ -f /init_disable_telnet ] && DISABLE_TELNET="y"
run_debug_session "init-debug in real rootfs" $HALT_BOOT
# If we don't do this then udev will not be able to create /dev/block/*
rm /dev/block
# Now try to boot the real init
# Prefer /sbin/preinit over /sbin/init
[ -x /sbin/preinit ] && INIT=/sbin/preinit || INIT=/sbin/init
exec $INIT --log-level=debug --log-target=kmsg &> /boot/systemd_stdouterr
run_debug_session "init in real rootfs failed"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment