7699f8d537
Currently we rely on the fact, that the kernel asks user space to load the firmware for it, so we can use the /lib/firmware/postmarketos directory for firmware. As the kernel has support for loading the firmware itself since v3.7, and the parameter for adding an additional path to the search paths was added with v3.10, we can use that to not rely on CONFIG_FW_LOADER_USER_HELPER being set.
436 lines
12 KiB
Bash
436 lines
12 KiB
Bash
#!/bin/sh
|
|
# This file will be in /init_functions.sh inside the initramfs.
|
|
IP=172.16.42.1
|
|
|
|
# Redirect stdout and stderr to logfile
|
|
setup_log() {
|
|
# Bail out if PMOS_NO_OUTPUT_REDIRECT is set
|
|
echo "### postmarketOS initramfs ###"
|
|
grep -q PMOS_NO_OUTPUT_REDIRECT /proc/cmdline && return
|
|
|
|
# Print a message about what is going on to the normal output
|
|
echo "NOTE: All output from the initramfs gets redirected to:"
|
|
echo "/pmOS_init.log"
|
|
echo "If you want to disable this behavior (e.g. because you're"
|
|
echo "debugging over serial), please add this to your kernel"
|
|
echo "command line: PMOS_NO_OUTPUT_REDIRECT"
|
|
|
|
# Start redirect, print the first line again
|
|
exec >/pmOS_init.log 2>&1
|
|
echo "### postmarketOS initramfs ###"
|
|
}
|
|
|
|
mount_proc_sys_dev() {
|
|
# mdev
|
|
mount -t proc -o nodev,noexec,nosuid proc /proc || echo "Couldn't mount /proc"
|
|
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys || echo "Couldn't mount /sys"
|
|
|
|
mkdir /config
|
|
mount -t configfs -o nodev,noexec,nosuid configfs /config
|
|
|
|
# /dev/pts (needed for telnet)
|
|
mkdir -p /dev/pts
|
|
mount -t devpts devpts /dev/pts
|
|
|
|
# /run (needed for cryptsetup)
|
|
mkdir /run
|
|
}
|
|
|
|
setup_firmware_path() {
|
|
# Add the postmarketOS-specific path to the firmware search paths.
|
|
# This should be sufficient on kernel 3.10+, before that we need
|
|
# the kernel calling udev (and in our case /usr/lib/firmwareload.sh)
|
|
# to load the firmware for the kernel.
|
|
echo "Configuring kernel firmware image search path"
|
|
SYS=/sys/module/firmware_class/parameters/path
|
|
if ! [ -e "$SYS" ]; then
|
|
echo "Kernel does not support setting the firmware image search path. Skipping."
|
|
return
|
|
fi
|
|
# shellcheck disable=SC2039
|
|
echo -n /lib/firmware/postmarketos >$SYS
|
|
}
|
|
|
|
setup_mdev() {
|
|
echo /sbin/mdev >/proc/sys/kernel/hotplug
|
|
mdev -s
|
|
}
|
|
|
|
mount_subpartitions() {
|
|
# Do not create subpartition mappings if pmOS_boot
|
|
# already exists (e.g. installed on an sdcard)
|
|
blkid |grep -q "pmOS_boot" && return
|
|
attempt_count=0
|
|
echo "Trying to mount subpartitions for 10 seconds..."
|
|
while [ -z "$(find_boot_partition)" ]; do
|
|
partitions="$(grep -v "loop\|ram" < /proc/diskstats |\
|
|
sed 's/\(\s\+[0-9]\+\)\+\s\+//;s/ .*//;s/^/\/dev\//')"
|
|
echo "$partitions" | while read -r partition; do
|
|
case "$(kpartx -l "$partition" 2>/dev/null | wc -l)" in
|
|
2)
|
|
echo "Mount subpartitions of $partition"
|
|
kpartx -afs "$partition"
|
|
# Ensure that this was the *correct* subpartition
|
|
# Some devices have mmc partitions that appear to have
|
|
# subpartitions, but aren't our subpartition.
|
|
if blkid | grep -q "pmOS_boot"; then
|
|
break
|
|
fi
|
|
kpartx -d "$partition"
|
|
continue
|
|
;;
|
|
*)
|
|
continue
|
|
;;
|
|
esac
|
|
done
|
|
attempt_count=$(( attempt_count + 1 ));
|
|
if [ "$attempt_count" -gt "100" ]; then
|
|
echo "ERROR: failed to mount subpartitions!"
|
|
return;
|
|
fi
|
|
sleep 0.1;
|
|
done
|
|
}
|
|
|
|
find_root_partition() {
|
|
# The partition layout is one of the following:
|
|
# a) boot, root partitions on sdcard
|
|
# b) boot, root partition on the "system" partition (which has its
|
|
# own partition header! so we have partitions on partitions!)
|
|
#
|
|
# mount_subpartitions() must get executed before calling
|
|
# find_root_partition(), so partitions from b) also get found.
|
|
|
|
# Try partitions in /dev/mapper and /dev/dm-* first
|
|
for id in pmOS_root crypto_LUKS; do
|
|
for path in /dev/mapper /dev/dm; do
|
|
DEVICE="$(blkid | grep "$path" | grep "$id" \
|
|
| cut -d ":" -f 1)"
|
|
[ -z "$DEVICE" ] || break 2
|
|
done
|
|
done
|
|
|
|
# Then try all devices
|
|
if [ -z "$DEVICE" ]; then
|
|
for id in pmOS_root crypto_LUKS; do
|
|
DEVICE="$(blkid | grep "$id" | cut -d ":" -f 1)"
|
|
[ -z "$DEVICE" ] || break
|
|
done
|
|
fi
|
|
echo "$DEVICE"
|
|
}
|
|
|
|
find_boot_partition() {
|
|
findfs LABEL="pmOS_boot"
|
|
}
|
|
|
|
mount_boot_partition() {
|
|
partition=$(find_boot_partition)
|
|
if [ -z "$partition" ]; then
|
|
echo "ERROR: boot partition not found!"
|
|
show_splash /splash-noboot.ppm.gz
|
|
loop_forever
|
|
fi
|
|
echo "Mount boot partition ($partition)"
|
|
mount -r "$partition" /boot
|
|
}
|
|
|
|
# $1: initramfs-extra path
|
|
extract_initramfs_extra() {
|
|
initramfs_extra="$1"
|
|
if [ ! -e "$initramfs_extra" ]; then
|
|
echo "ERROR: initramfs-extra not found!"
|
|
show_splash /splash-noinitramfsextra.ppm.gz
|
|
loop_forever
|
|
fi
|
|
echo "Extract $initramfs_extra"
|
|
gzip -d -c "$initramfs_extra" | cpio -i
|
|
}
|
|
|
|
wait_root_partition() {
|
|
while [ -z "$(find_root_partition)" ]; do
|
|
show_splash /splash-norootfs.ppm.gz
|
|
echo "Could not find the rootfs."
|
|
echo "Maybe you need to insert the sdcard, if your device has"
|
|
echo "any? Trying again in one second..."
|
|
sleep 1
|
|
done
|
|
}
|
|
|
|
resize_root_partition() {
|
|
partition=$(find_root_partition)
|
|
# Only resize the partition if it's inside the device-mapper, which means
|
|
# that the partition is stored as a subpartition inside another one.
|
|
# In this case we want to resize it to use all the unused space of the
|
|
# external partition.
|
|
if [ -z "${partition##"/dev/mapper/"*}" ]; then
|
|
# Get physical device
|
|
partition_dev=$(dmsetup deps -o devname "$partition" | \
|
|
awk -F "[()]" '{print "/dev/"$2}')
|
|
# Check if there is unallocated space at the end of the device
|
|
if parted -s "$partition_dev" print free | tail -n2 | \
|
|
head -n1 | grep -qi "free space"; then
|
|
echo "Resize root partition ($partition)"
|
|
# unmount subpartition, resize and remount it
|
|
kpartx -d "$partition"
|
|
parted -s "$partition_dev" resizepart 2 100%
|
|
kpartx -afs "$partition_dev"
|
|
fi
|
|
fi
|
|
# Resize the root partition (non-subpartitions). Usually we do not want this,
|
|
# except for QEMU devices (where PMOS_FORCE_PARTITION_RESIZE gets passed as
|
|
# kernel parameter).
|
|
if grep -q PMOS_FORCE_PARTITION_RESIZE /proc/cmdline; then
|
|
echo "Resize root partition ($partition)"
|
|
parted -s "$(echo "$partition" | sed -E 's/p?2$//')" resizepart 2 100%
|
|
partprobe
|
|
fi
|
|
}
|
|
|
|
unlock_root_partition() {
|
|
partition="$(find_root_partition)"
|
|
if cryptsetup isLuks "$partition"; then
|
|
until cryptsetup status root | grep -qwi active; do
|
|
start_onscreen_keyboard
|
|
done
|
|
# Show again the loading splashscreen
|
|
show_splash /splash-loading.ppm.gz
|
|
fi
|
|
}
|
|
|
|
resize_root_filesystem() {
|
|
partition="$(find_root_partition)"
|
|
touch /etc/mtab # see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=673323
|
|
echo "Check/repair root filesystem ($partition)"
|
|
e2fsck -f -y "$partition"
|
|
echo "Resize root filesystem ($partition)"
|
|
resize2fs -f "$partition"
|
|
}
|
|
|
|
mount_root_partition() {
|
|
partition="$(find_root_partition)"
|
|
echo "Mount root partition ($partition)"
|
|
mount -t ext4 -o ro "$partition" /sysroot
|
|
if ! [ -e /sysroot/usr ]; then
|
|
echo "ERROR: unable to mount root partition!"
|
|
show_splash /splash-mounterror.ppm.gz
|
|
loop_forever
|
|
fi
|
|
}
|
|
|
|
setup_usb_network_android() {
|
|
# Only run, when we have the android usb driver
|
|
SYS=/sys/class/android_usb/android0
|
|
if ! [ -e "$SYS" ]; then
|
|
echo " /sys/class/android_usb does not exist, skipping android_usb"
|
|
return
|
|
fi
|
|
|
|
echo " Setting up an USB gadget through android_usb"
|
|
|
|
# Do the setup
|
|
printf "%s" "0" >"$SYS/enable"
|
|
printf "%s" "18D1" >"$SYS/idVendor"
|
|
printf "%s" "D001" >"$SYS/idProduct"
|
|
printf "%s" "rndis" >"$SYS/functions"
|
|
printf "%s" "1" >"$SYS/enable"
|
|
}
|
|
|
|
setup_usb_network_configfs() {
|
|
CONFIGFS=/config/usb_gadget
|
|
|
|
if ! [ -e "$CONFIGFS" ]; then
|
|
echo " /config/usb_gadget does not exist, skipping configfs usb gadget"
|
|
return
|
|
fi
|
|
|
|
echo " Setting up an USB gadget through configfs"
|
|
# Create an usb gadet configuration
|
|
mkdir $CONFIGFS/g1 || echo " Couldn't create $CONFIGFS/g1"
|
|
printf "%s" "0x18D1" >"$CONFIGFS/g1/idVendor"
|
|
printf "%s" "0xD001" >"$CONFIGFS/g1/idProduct"
|
|
|
|
# Create english (0x409) strings
|
|
mkdir $CONFIGFS/g1/strings/0x409 || echo " Couldn't create $CONFIGFS/g1/strings/0x409"
|
|
echo "postmarketOS" > "$CONFIGFS/g1/strings/0x409/manufacturer"
|
|
echo "Debug network interface" > "$CONFIGFS/g1/strings/0x409/product"
|
|
|
|
# Create rndis function
|
|
mkdir $CONFIGFS/g1/functions/rndis.usb0 || echo " Couldn't create $CONFIGFS/g1/functions/rndis.usb0"
|
|
|
|
# Create configuration instance for the gadget
|
|
mkdir $CONFIGFS/g1/configs/c.1 || echo " Couldn't create $CONFIGFS/g1/configs/c.1"
|
|
mkdir $CONFIGFS/g1/configs/c.1/strings/0x409 || echo " Couldn't create $CONFIGFS/g1/configs/c.1/strings/0x409"
|
|
printf "%s" "rndis" > $CONFIGFS/g1/configs/c.1/strings/0x409/configuration || echo " Couldn't write configration name"
|
|
|
|
# Link the rndis instance to the configuration
|
|
ln -s $CONFIGFS/g1/functions/rndis.usb0 $CONFIGFS/g1/configs/c.1 || echo " Couldn't symlink rndis.usb0"
|
|
|
|
# Check if there's an USB Device Controller
|
|
if [ -z "$(ls /sys/class/udc)" ]; then
|
|
echo " No USB Device Controller available"
|
|
return
|
|
fi
|
|
|
|
# Link the gadget instance to an USB Device Controller
|
|
# See also: https://github.com/postmarketOS/pmbootstrap/issues/338
|
|
# shellcheck disable=SC2005
|
|
echo "$(ls /sys/class/udc)" > $CONFIGFS/g1/UDC || echo " Couldn't write UDC"
|
|
}
|
|
|
|
setup_usb_network() {
|
|
# Only run once
|
|
_marker="/tmp/_setup_usb_network"
|
|
[ -e "$_marker" ] && return
|
|
touch "$_marker"
|
|
echo "Setup usb network"
|
|
# Run all usb network setup functions (add more below!)
|
|
setup_usb_network_android
|
|
setup_usb_network_configfs
|
|
}
|
|
|
|
start_udhcpd() {
|
|
# Only run once
|
|
[ -e /etc/udhcpd.conf ] && return
|
|
|
|
# Skip if disabled
|
|
# shellcheck disable=SC2154
|
|
if [ "$deviceinfo_disable_dhcpd" = "true" ]; then
|
|
echo "NOTE: start of dhcpd is disabled (deviceinfo_disable_dhcpd)"
|
|
touch /etc/udhcpcd.conf
|
|
return
|
|
fi
|
|
|
|
echo "Starting udhcpd"
|
|
# Get usb interface
|
|
INTERFACE=""
|
|
ifconfig rndis0 "$IP" 2>/dev/null && INTERFACE=rndis0
|
|
if [ -z $INTERFACE ]; then
|
|
ifconfig usb0 "$IP" 2>/dev/null && INTERFACE=usb0
|
|
fi
|
|
if [ -z $INTERFACE ]; then
|
|
ifconfig eth0 "$IP" 2>/dev/null && INTERFACE=eth0
|
|
fi
|
|
|
|
if [ -z $INTERFACE ]; then
|
|
echo " Could not find an interface to run a dhcp server on"
|
|
echo " Interfaces:"
|
|
ip link
|
|
return
|
|
fi
|
|
|
|
echo " Using interface $INTERFACE"
|
|
|
|
# Create /etc/udhcpd.conf
|
|
{
|
|
echo "start 172.16.42.2"
|
|
echo "end 172.16.42.2"
|
|
echo "auto_time 0"
|
|
echo "decline_time 0"
|
|
echo "conflict_time 0"
|
|
echo "lease_file /var/udhcpd.leases"
|
|
echo "interface $INTERFACE"
|
|
echo "option subnet 255.255.255.0"
|
|
} >/etc/udhcpd.conf
|
|
|
|
echo " Start the dhcpcd daemon (forks into background)"
|
|
udhcpd
|
|
}
|
|
|
|
setup_directfb_tslib(){
|
|
# Set up directfb and tslib
|
|
# Note: linux_input module is disabled since it will try to take over
|
|
# the touchscreen device from tslib (e.g. on the N900)
|
|
export DFBARGS="system=fbdev,no-cursor,disable-module=linux_input"
|
|
# shellcheck disable=SC2154
|
|
if [ ! -z "$deviceinfo_dev_touchscreen" ]; then
|
|
export TSLIB_TSDEVICE="$deviceinfo_dev_touchscreen"
|
|
fi
|
|
}
|
|
|
|
start_onscreen_keyboard(){
|
|
setup_directfb_tslib
|
|
osk-sdl -n root -d "$partition" -c /etc/osk.conf -v > /osk-sdl.log 2>&1
|
|
unset DFBARGS
|
|
unset TSLIB_TSDEVICE
|
|
}
|
|
|
|
start_charging_mode(){
|
|
# Check cmdline for charging mode
|
|
chargingmodes="
|
|
androidboot.mode=charger
|
|
lpm_boot=1
|
|
androidboot.huawei_type=oem_rtc
|
|
startup=0x00010004
|
|
lpcharge=1
|
|
androidboot.bootchg=true
|
|
"
|
|
# shellcheck disable=SC2086
|
|
grep -Eq "$(echo $chargingmodes | tr ' ' '|')" /proc/cmdline || return
|
|
setup_directfb_tslib
|
|
# Get the font from osk-sdl config
|
|
fontpath=$(awk '/^keyboard-font/{print $3}' /etc/osk.conf)
|
|
# Set up triggerhappy config
|
|
{
|
|
echo "KEY_POWER 1 pgrep -x charging-sdl || charging-sdl -pcf $fontpath"
|
|
} >/etc/triggerhappy.conf
|
|
# Start it once and then start triggerhappy
|
|
(
|
|
charging-sdl -pcf "$fontpath" \
|
|
|| show_splash /splash-charging-error.ppm.gz
|
|
) &
|
|
thd --deviceglob /dev/input/event* --triggers /etc/triggerhappy.conf
|
|
}
|
|
|
|
# $1: path to ppm.gz file
|
|
show_splash() {
|
|
# Skip for non-framebuffer devices
|
|
# shellcheck disable=SC2154
|
|
if [ "$deviceinfo_no_framebuffer" = "true" ]; then
|
|
echo "NOTE: Skipping framebuffer splashscreen (deviceinfo_no_framebuffer)"
|
|
return
|
|
fi
|
|
|
|
gzip -c -d "$1" >/tmp/splash.ppm
|
|
fbsplash -s /tmp/splash.ppm
|
|
}
|
|
|
|
set_framebuffer_mode() {
|
|
[ -e "/sys/class/graphics/fb0/modes" ] || return
|
|
[ -z "$(cat /sys/class/graphics/fb0/mode)" ] || return
|
|
|
|
_mode="$(cat /sys/class/graphics/fb0/modes)"
|
|
echo "Setting framebuffer mode to: $_mode"
|
|
echo "$_mode" > /sys/class/graphics/fb0/mode
|
|
}
|
|
|
|
setup_framebuffer() {
|
|
# Skip for non-framebuffer devices
|
|
# shellcheck disable=SC2154
|
|
if [ "$deviceinfo_no_framebuffer" = "true" ]; then
|
|
echo "NOTE: Skipping framebuffer setup (deviceinfo_no_framebuffer)"
|
|
return
|
|
fi
|
|
|
|
# Wait for /dev/fb0
|
|
echo "NOTE: Waiting 10 seconds for the framebuffer /dev/fb0."
|
|
echo "If your device does not have a framebuffer, disable this with:"
|
|
echo "no_framebuffer=true in <https://postmarketos.org/deviceinfo>"
|
|
for _ in $(seq 1 100); do
|
|
[ -e "/dev/fb0" ] && break
|
|
sleep 0.1
|
|
done
|
|
if ! [ -e "/dev/fb0" ]; then
|
|
echo "ERROR: /dev/fb0 did not appear!"
|
|
return
|
|
fi
|
|
|
|
set_framebuffer_mode
|
|
}
|
|
|
|
loop_forever() {
|
|
while true; do
|
|
sleep 1
|
|
done
|
|
}
|