diff --git a/.ci/shellcheck.sh b/.ci/shellcheck.sh
index ccd709723170fa7545824edf488a3f9be7cc82d1..1dcc32f304e051c2355f3c3b515233e8cca5a785 100755
--- a/.ci/shellcheck.sh
+++ b/.ci/shellcheck.sh
@@ -20,7 +20,6 @@ sh_files="
 	./main/postmarketos-installkernel/installkernel-pmos
 	./main/postmarketos-initramfs/init.sh
 	./main/postmarketos-initramfs/init_functions.sh
-	./main/postmarketos-mkinitfs-hook-debug-shell/20-debug-shell.sh
 	./main/postmarketos-mkinitfs-hook-debug-shell/setup_usb_storage.sh
 	./main/postmarketos-mkinitfs-hook-netboot/netboot.sh
 	./main/postmarketos-update-kernel/update-kernel.sh
diff --git a/main/postmarketos-initramfs/APKBUILD b/main/postmarketos-initramfs/APKBUILD
index 5ae715b77388286645f4eb482fbedd173f87d34e..dd7a5d8ca743e44bb6fea7181b079e0ac3239b50 100644
--- a/main/postmarketos-initramfs/APKBUILD
+++ b/main/postmarketos-initramfs/APKBUILD
@@ -1,7 +1,7 @@
 # Maintainer: Oliver Smith <ollieparanoid@postmarketos.org>
 # Co-Maintainer: Clayton Craft <clayton@craftyguy.net>
 pkgname=postmarketos-initramfs
-pkgver=2.5.6
+pkgver=2.6.0
 pkgrel=0
 pkgdesc="Base files for the postmarketOS initramfs / initramfs-extra"
 url="https://postmarketos.org"
@@ -32,6 +32,7 @@ source="
 	00-default.modules
 	00-initramfs-base.dirs
 	00-initramfs-base.files
+	00-initramfs-fullsize.files
 	00-initramfs-extra-base.files
 	init.sh
 	init_functions.sh
@@ -93,6 +94,7 @@ tiny() {
 
 # Full size initramfs for most devices
 fullsize() {
+	depends="iskey"
 	install_if="postmarketos-initramfs"
 
 	mkdir -p "$subpkgdir"
@@ -100,15 +102,19 @@ fullsize() {
 	# be used for dependencies which don't fit in the tiny variant.
 	# Those dependencies should then be adjusted to be optional at
 	# runtime, so that the tiny variant can still be used.
+
+	install -Dm644 "$srcdir"/00-initramfs-fullsize.files \
+		-t "$pkgdir"/usr/share/mkinitfs/files/
 }
 
 sha512sums="
 59be0649ed87a72d93624bd8a2e3f8c99a0f32f7b7a26f99436de782beba55671472c269eeee86440efc87e0d7148a0bb335fa537791092e73878ca21330544a  00-default.modules
 5b364300f31c91fd0591eb0715f67cbf5383f45246a5fb9f34b79f7cb2e3b15768b2130e5f32f816cc169950f988c1beabc879ba31645c58ce131a288dbc071d  00-initramfs-base.dirs
 ab41b45b0613f25a61114ed8c8b92bc53c60838f6e2e0ba18c76e5369b2984e6023a0661887692673aca3f647f268c468a468f6b1ac424cfee609017a89481dd  00-initramfs-base.files
+ede9eb44fe180578ca97b7abfa2c4f1b4e277a49e4ca8b8911005acd844dbce38f200bc84280ded5a35fe5abd32bec391efc4af1929db12aa52da0c1036c6983  00-initramfs-fullsize.files
 8a4adad3785af474b36a09a05f6a3b2c4b4f43aac331a53b903abfa51ea12be1e3d1d807b7a6e66a1346815f3b0044daf8cd62e21e2dc75d2db13ee265a72985  00-initramfs-extra-base.files
-b42d7cceac11f4d017b9f8eaa86ec34c4924cc04f61cec7dfd5f0b30465b3a019a1d7fc50a13f3df53531f2afc314f50ac74611d6fd5ecf91c293039bcdc2fa8  init.sh
-71c180ef0030e4dec2e20336efa354d08ced8cbf6825b1c0bdb527b349040c73fdc87c287e542c68bca1bb4678266a1fb5dd869b538fced032361c7c887b7894  init_functions.sh
+38acb5db6fc7e4f0b94a54f6293121dd5cb4321c446c50b855ac1d3ec782642ac3119933688ab041e69ef6ea8e53f60e040b970f7e032a8b6dc603cd29e88812  init.sh
+ea96fe57ac0c02b592fe7c8ea42561502bda286e1500e9f3f70810f4edecb333e3a207951bb4f25b3d2222e0d8cfe6876e0a94232254e9721661273bd21f3858  init_functions.sh
 ba3275a9af788c7c782322a22a0f144d5e50e3498ea6886486a29331f23ae89cd32d500a3635cfa7cab369afba92edc18aeca64ccbf0cd589061cce23d15b46c  unudhcpd.conf
 675e7d5bee39b2df7d322117f8dcaccc274d61beaf4d50ead19bbf2109446d64b1c0aa0c5b4f9846eb6c1c403418f28f6364eff4537ba41120fbfcbc484b7da7  mdev.conf
 "
diff --git a/main/postmarketos-initramfs/init.sh b/main/postmarketos-initramfs/init.sh
index c047fb07393d8ff4e1150b36fbb5ed5ed21c74e4..57ac2efc0fc6dbf8c4be9b9bafde32c765b3e19d 100644
--- a/main/postmarketos-initramfs/init.sh
+++ b/main/postmarketos-initramfs/init.sh
@@ -46,12 +46,17 @@ if [ "$IN_CI" = "true" ]; then
 	fail_halt_boot
 fi
 
-# Always run dhcp daemon/usb networking for now (later this should only
-# be enabled, when having the debug-shell hook installed for debugging,
-# or get activated after the initramfs is done with an OpenRC service).
 setup_usb_network
 start_unudhcpd
 
+# if iskey isn't available then symlink false -- assume no keys
+# are pressed
+if [ ! -x /usr/bin/iskey ]; then
+	ln -s /bin/false /usr/bin/iskey
+fi
+
+check_keys true
+
 mount_subpartitions
 
 wait_boot_partition
@@ -95,6 +100,13 @@ echo ratelimit > /proc/sys/kernel/printk_devkmsg
 
 killall telnetd mdev udevd msm-fb-refresher syslogd 2>/dev/null
 
+# Kill any getty shells that might be running
+for pid in $(pidof sh); do
+	if ! [ "$pid" = "1" ]; then
+		kill -9 "$pid"
+	fi
+done
+
 # shellcheck disable=SC2093
 exec switch_root /sysroot "$init"
 
diff --git a/main/postmarketos-initramfs/init_functions.sh b/main/postmarketos-initramfs/init_functions.sh
index 6b5695e6db59384e22cc771f9394a0fa536f7f58..7116ff94b22ee6e4f09569ff84a42a7886babfe6 100644
--- a/main/postmarketos-initramfs/init_functions.sh
+++ b/main/postmarketos-initramfs/init_functions.sh
@@ -6,6 +6,9 @@ PMOS_BOOT=""
 PMOS_ROOT=""
 
 CONFIGFS=/config/usb_gadget
+CONFIGFS_ACM_FUNCTION="acm.usb0"
+
+deviceinfo_codename=""
 
 # Redirect stdout and stderr to logfile
 setup_log() {
@@ -732,6 +735,155 @@ start_unudhcpd() {
 	) &
 }
 
+setup_usb_acm_configfs() {
+	active_udc="$(cat $CONFIGFS/g1/UDC)"
+
+	if ! [ -e "$CONFIGFS" ]; then
+		echo "  /config/usb_gadget does not exist, can't set up serial gadget"
+		return 1
+	fi
+
+	# unset UDC
+	echo "" > /config/usb_gadget/g1/UDC
+
+	# Create acm function
+	mkdir "$CONFIGFS/g1/functions/$CONFIGFS_ACM_FUNCTION" \
+		|| echo "  Couldn't create $CONFIGFS/g1/functions/$CONFIGFS_ACM_FUNCTION"
+
+	# Link the acm function to the configuration
+	ln -s "$CONFIGFS/g1/functions/$CONFIGFS_ACM_FUNCTION" "$CONFIGFS/g1/configs/c.1" \
+		|| echo "  Couldn't symlink $CONFIGFS_ACM_FUNCTION"
+
+	# Reconfigure the UDC
+	setup_usb_configfs_udc
+
+	return 0
+}
+
+# Spawn a subshell to restart the getty if it exits
+# $1: tty
+run_getty() {
+	{
+		while /sbin/getty -n -l /sbin/pmos_getty "$1" 115200 vt100; do
+			sleep 0.2
+		done
+	} &
+}
+
+debug_shell() {
+	echo "Entering debug shell"
+	setup_usb_acm_configfs
+
+	# mount pstore, if possible
+	if [ -d /sys/fs/pstore ]; then
+		mount -t pstore pstore /sys/fs/pstore || true
+	fi
+
+	mount -t debugfs none /sys/kernel/debug || true
+	# make a symlink like Android recoveries do
+	ln -s /sys/kernel/debug /d
+
+	cat <<-EOF > /README
+	postmarketOS debug shell
+	https://postmarketos.org/debug-shell
+
+	  Kernel: $(uname -r)
+	  Device: $deviceinfo_codename
+	  OS ver: $VERSION
+	  initrd: $INITRAMFS_PKG_VERSION
+
+	Run 'pmos_continue_boot' to continue booting.
+	Run 'pmos_logdump' to generate a log dump and expose it over USB.
+	EOF
+
+	if [ -f /usr/bin/setup_usb_storage ]; then
+		cat <<-EOF >> /README
+		You can expose storage devices over USB with
+		'setup_usb_storage /dev/DEVICE'
+		EOF
+	fi
+
+	# Display some info
+	cat <<-EOF > /etc/profile
+	cat /README
+	. /init_functions.sh
+	EOF
+
+	cat <<-EOF > /sbin/pmos_getty
+	#!/bin/sh
+	exec /bin/sh -l
+	EOF
+	chmod +x /sbin/pmos_getty
+
+	cat <<-EOF > /sbin/pmos_continue_boot
+	#!/bin/sh
+	echo "Continuing boot..."
+	touch /tmp/continue_boot
+	while sleep 1; do :; done
+	EOF
+	chmod +x /sbin/pmos_continue_boot
+
+	cat <<-EOF > /sbin/pmos_logdump
+	#!/bin/sh
+	echo "Dumping logs, check for a new mass storage device"
+	touch /tmp/dump_logs
+	EOF
+	chmod +x /sbin/pmos_logdump
+
+	# Get the console (ttyX) associated with /dev/console
+	active_console="$(cat /sys/devices/virtual/tty/console/active)"
+
+	# Spawn a getty on the active console
+	run_getty "$active_console"
+
+	# Spawn fbkeyboard if installed
+	if [ -x /usr/bin/fbkeyboard ]; then
+		modprobe uinput
+		fbkeyboard -r "$(cat /sys/class/graphics/fbcon/rotate)" \
+			2>/dev/tty1 &
+		if [ "$active_console" != "tty1" ]; then
+			run_getty tty1
+		fi
+	fi
+
+	# And on the usb acm port (if it exists)
+	if [ -e /dev/ttyGS0 ] && [ "$active_console" != "ttyGS0" ]; then
+		run_getty ttyGS0
+	fi
+
+	show_splash "WARNING: debug-shell is active\\nhttps://postmarketos.org/debug-shell"
+
+	# wait until we get the signal to continue boot
+	while ! [ -e /tmp/continue_boot ]; do
+		sleep 0.2
+		if [ -e /tmp/dump_logs ]; then
+			rm -f /tmp/dump_logs
+			export_logs
+		fi
+	done
+
+	# Remove the ACM gadget device
+	rm -f $CONFIGFS/g1/configs/c.1/"$CONFIGFS_ACM_FUNCTION"
+	rmdir $CONFIGFS/g1/functions/"$CONFIGFS_ACM_FUNCTION"
+	setup_usb_configfs_udc
+
+	pkill -f fbkeyboard || true
+}
+
+# Check if the user is pressing a key and either drop to a shell or halt boot as applicable
+# $1: If set, also trigger debug shell if "pmos.debug-shell" is in kernel cmdline
+check_keys() {
+	# If the user is pressing either the left control key or the volume down
+	# key then drop to a debug shell.
+	if { [ -n "$1" ] && grep -q "pmos.debug-shell" /proc/cmdline; } || iskey KEY_LEFTCTRL KEY_VOLUMEDOWN; then
+		debug_shell
+	# If instead they're pressing left shift or volume up, then fail boot
+	# and dump logs
+	elif iskey KEY_LEFTSHIFT KEY_VOLUMEUP; then
+		fail_halt_boot
+	fi
+}
+
 # $1: Message to show
 show_splash() {
 	# Skip for non-framebuffer devices
diff --git a/main/postmarketos-mkinitfs-hook-console-shell/APKBUILD b/main/postmarketos-mkinitfs-hook-console-shell/APKBUILD
index ef7eeb232cf2d546e5991d1aa98c80896e66048d..853ab3b180e0f3e852783b9dcf9443b46d60cdeb 100644
--- a/main/postmarketos-mkinitfs-hook-console-shell/APKBUILD
+++ b/main/postmarketos-mkinitfs-hook-console-shell/APKBUILD
@@ -1,18 +1,16 @@
 # Maintainer: Ferenc Bakonyi <bakonyi.ferenc@gmail.com>
 pkgname=postmarketos-mkinitfs-hook-console-shell
-pkgver=0.3.1
+pkgver=0.4.0
 pkgrel=0
 pkgdesc="Root console shell in the initramfs (security hole, for debugging only)"
 url="https://postmarketos.org"
 depends="postmarketos-mkinitfs devicepkg-utils fbdebug evtest linuxconsoletools reboot-mode fbkeyboard font-dejavu"
-source="console-shell.sh console-shell.files console-shell.modules"
+source="console-shell.files console-shell.modules"
 arch="noarch"
 license="GPL-2.0-or-later"
 options="!check" # No tests
 
 package() {
-	install -Dm644 "$srcdir"/console-shell.sh \
-		"$pkgdir"/usr/share/mkinitfs/hooks/30-console-shell.sh
 	install -Dm644 "$srcdir"/console-shell.files \
 		"$pkgdir"/usr/share/mkinitfs/files/30-console-shell.files
 	install -Dm644 "$srcdir"/console-shell.modules \
@@ -20,7 +18,6 @@ package() {
 }
 
 sha512sums="
-0753bf2e2bb011309a906db08b5fc13dba90e55bd5d11c8da0c34c89faf04dbf5915785cc0ed2a164f2e4c5fede06c7e80b5f592f4deb306cf5cec2371ef88f9  console-shell.sh
 17b65cb24103e4c1459ae72bc036c1f06cdfcccf480389ecf6a28253d104b9b06d394cf53314a1ef4ace4ffc88b6b1384ef4894b7270d6b2cfdfc83592e12b2c  console-shell.files
 a9b069ed121ffeee887e0583d8cb46035ecf1fa90a26a4ecb3aa11ff03178b2b08621f6676db6b2350f290694c04aabcf36f2ce3e0813a76dde9a33555edb112  console-shell.modules
 "
diff --git a/main/postmarketos-mkinitfs-hook-console-shell/console-shell.sh b/main/postmarketos-mkinitfs-hook-console-shell/console-shell.sh
deleted file mode 100644
index 62ad9388204757337673f4597511e2b52464b5f6..0000000000000000000000000000000000000000
--- a/main/postmarketos-mkinitfs-hook-console-shell/console-shell.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/sh
-# shellcheck disable=SC1091
-. ./init_functions.sh
-. /usr/share/misc/source_deviceinfo
-
-# mount pstore, if possible
-if [ -d /sys/fs/pstore ]; then
-	mount -t pstore pstore /sys/fs/pstore || true
-fi
-
-if tty -s; then
-	tty=/dev/tty0
-	modprobe uinput
-	fbkeyboard -r $(cat /sys/class/graphics/fbcon/rotate) 2>$tty &
-	echo "Exit the shell to continue booting:" > $tty
-	sh +m <$tty >$tty 2>$tty
-	pkill -f fbkeyboard
-else
-	echo "No tty attached, exiting."
-fi
diff --git a/main/postmarketos-mkinitfs-hook-debug-shell/20-debug-shell.sh b/main/postmarketos-mkinitfs-hook-debug-shell/20-debug-shell.sh
deleted file mode 100644
index 59496c503e9c2bb2e2565eab6d42848cc19ab38a..0000000000000000000000000000000000000000
--- a/main/postmarketos-mkinitfs-hook-debug-shell/20-debug-shell.sh
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/bin/sh
-# shellcheck disable=SC1091
-. ./init_functions.sh
-. /usr/share/misc/source_deviceinfo
-TELNET_PORT=23
-
-setup_usb_network
-start_unudhcpd
-
-show_splash "WARNING: debug-shell is active\\nhttps://postmarketos.org/debug-shell"
-
-echo "Create 'pmos_continue_boot' script"
-{
-	echo "#!/bin/sh"
-	#Disable any active usb mass storage
-	echo "if [ -d /config/usb_gadget/g1/functions/mass_storage.0 ]; then setup_usb_storage; fi"
-	echo "pkill -9 -f pmos_shell"
-	echo "pkill -f pmos_fail_halt_boot"
-	echo "pkill -f telnetd.*:${TELNET_PORT}"
-} >/usr/bin/pmos_continue_boot
-chmod +x /usr/bin/pmos_continue_boot
-
-echo "Create 'pmos_shell' script"
-{
-	echo "#!/bin/sh"
-	echo "sh"
-} >/usr/bin/pmos_shell
-chmod +x /usr/bin/pmos_shell
-
-echo "Create 'pmos_fail_halt_boot' script"
-{
-	echo "#!/bin/sh"
-	echo '. /init_functions.sh'
-	echo "fail_halt_boot"
-} >/usr/bin/pmos_fail_halt_boot
-chmod +x /usr/bin/pmos_fail_halt_boot
-
-echo "Start the telnet daemon"
-{
-	echo "#!/bin/sh"
-	echo "echo \"Type 'pmos_continue_boot' to continue booting:\""
-	echo "sh"
-} >/telnet_connect.sh
-chmod +x /telnet_connect.sh
-
-host_ip="${unudhcpd_host_ip:-172.16.42.1}"
-telnetd -b "${host_ip}:${TELNET_PORT}" -l /telnet_connect.sh
-
-# mount pstore, if possible
-if [ -d /sys/fs/pstore ]; then
-	mount -t pstore pstore /sys/fs/pstore || true
-fi
-# mount debugfs - very helpful for debugging
-mount -t debugfs none /sys/kernel/debug || true
-# make a symlink like Android recoveries do
-ln -s /sys/kernel/debug /d
-
-echo "---"
-echo "WARNING: debug-shell is active on ${host_ip}:${TELNET_PORT}."
-echo "This is a security hole! Only use it for debugging, and"
-echo "uninstall the debug-shell hook afterwards!"
-echo "You can expose storage devices using 'setup_usb_storage /dev/DEVICE'"
-echo "---"
-
-pmos_shell
-
-# Show "Loading" splash again when continuing
-show_splash "Loading..."
diff --git a/main/postmarketos-mkinitfs-hook-debug-shell/APKBUILD b/main/postmarketos-mkinitfs-hook-debug-shell/APKBUILD
index 46d017c2261088e0f6061d9a1214d4f754b5101d..2569235d6ab123540d99cc4259e41739b904839c 100644
--- a/main/postmarketos-mkinitfs-hook-debug-shell/APKBUILD
+++ b/main/postmarketos-mkinitfs-hook-debug-shell/APKBUILD
@@ -1,17 +1,15 @@
 pkgname=postmarketos-mkinitfs-hook-debug-shell
-pkgver=0.6.0
+pkgver=0.7.0
 pkgrel=0
 pkgdesc="Root shell in the initramfs (security hole, for debugging only)"
 url="https://postmarketos.org"
 depends="postmarketos-mkinitfs devicepkg-utils fbdebug evtest linuxconsoletools reboot-mode libinput libinput-tools"
-source="20-debug-shell.sh 20-debug-shell.files setup_usb_storage.sh"
+source="20-debug-shell.files setup_usb_storage.sh"
 arch="noarch"
 license="GPL2"
 options="!check"
 
 package() {
-	install -Dm644 "$srcdir"/20-debug-shell.sh \
-		"$pkgdir"/usr/share/mkinitfs/hooks/20-debug-shell.sh
 	install -Dm644 "$srcdir"/20-debug-shell.files \
 		"$pkgdir"/usr/share/mkinitfs/files/20-debug-shell.files
 	install -Dm755 "$srcdir"/setup_usb_storage.sh \
@@ -19,7 +17,6 @@ package() {
 }
 
 sha512sums="
-add95b3fa64b804fd386298406fbc55489e3e122d70f40e45627a450d299e148a346a4a147c2b28e0110ecef14e9e0b62ac75a0bd2d39295c2407ebb5e93708c  20-debug-shell.sh
 a739bc47d905d189edb26d9ebfd062023720fefdaab27207471c16d53a9c12ea8b81092a1047d8f2300e42ba500bdf6c5a3343aca55aab5bf8e84d68eb5680ab  20-debug-shell.files
 75d485c2e9f352cfd717b7a92753a9dfc4a72526a44bcbb784eacb4ef9011072b3ffa1c42a317c0940598cc076fb6c61676c440e5b188378b19ca08d882c1338  setup_usb_storage.sh
 "