2022–02–22:
Adding LibreELEC.tv to an existing Pinephone multi-distro image
I wanted to try LibreELEC.tv on Pinephone, just for fun, and to see if it
will make Pinephone into a nice portable TV. :)
LibreELEC.tv has support for Pine64 A64 SBC, so the system should work fine.
The image can be downloaded here: https://libreelec.tv/downloads/allwinner/
None of the images are for Pinephone, though. If you download the image, and
extract it, you'll find that it contains kernel build and the DTB for the target
board only. :(
Anyway, that will not stop us from trying. Coincidentally, I wanted to write
about how easy it is to modify existing multi-boot image, and this is a very
good opportunity to demonstrate that.
Adding a new
distro to existing multi-boot image
My Pinephone multi-boot image already includes a kernel that works on
Pinephone. The only thing we'll have to achieve is to extract root filesystem
from the LibreELEC.tv image for Pine64 SBC (see above), and copy the files from
it to multi-boot image. Then we'll have to somehow add a boot entry for the new
distro, and that will be it.
Getting
LibreELEC.tv rootfs contents from the image
Let's start by extracting the rootfs from LibreELEC.tv image. Get the
image first:
wget 'https://releases.libreelec.tv/LibreELEC-A64.arm-10.0.1-pine64.img.gz'
It's gzipped, so ungzip it:
gzip -d LibreELEC-A64.arm-10.0.1-pine64.img.gz
The image is partitioned as you can see using sfdisk
for
example:
sfdisk --dump LibreELEC-A64.arm-10.0.1-pine64.img
Prints:
label: dos
label-id: 0x179a0c97
device: LibreELEC-A64.arm-10.0.1-pine64.img
unit: sectors
sector-size: 512
LibreELEC-A64.arm-10.0.1-pine64.img1 : start= 8192, size= 1048576, type=c, bootable
LibreELEC-A64.arm-10.0.1-pine64.img2 : start= 1056768, size= 65536, type=83
Let's see what's on the first partition…
dd if=LibreELEC-A64.arm-10.0.1-pine64.img of=LibreELEC-A64.arm-10.0.1-pine64.img1 skip=8192 count=1048576
7z x -obootfs LibreELEC-A64.arm-10.0.1-pine64.img1
tree bootfs
Tree will print us:
bootfs
├── extlinux
│ └── extlinux.conf
├── KERNEL
├── KERNEL.md5
├── overlays
│ ├── sun50i-a64-ir.dtbo
│ ├── sun50i-a64-pine64-audio-board.dtbo
│ ├── sun50i-a64-pine64-wifi-bt.dtbo
│ └── sun50i-a64-spdif.dtbo
├── sun50i-a64-pine64.dtb
├── SYSTEM
└── SYSTEM.md5
2 directories, 10 files
Looks like some typical kernel/DTB files and booltoader configuration
file,… aaand some suspect SYSTEM
file. Check it out:
file bootfs/SYSTEM
And it looks like it's SquashFS image:
bootfs/SYSTEM: Squashfs filesystem, little endian, version 4.0, zstd compressed, \
111496993 bytes, 9715 inodes, blocksize: 1048576 bytes, created: Fri Oct 29 20:07:52 2021
If we extract it, we will get what we were for: root filesystem contents.
Let's leave that for later.
Add
new boot entry and the distribution subvolume to the multi-boot image
Multi-boot image has two partitions. The first one contains the boot
filesystem with the kernel and other files needed by the bootloader, and the
second one contains BTRFS filesystem with one subvolume per distro. During boot,
the same kernel is used for all boot options, and which subvolume is used is
deterimned by the rootflags=subvol=name
kernel boot argument. With
BTRFS, you can either mount the toplevel subvolume, or any other subvolume by
name if you specify a subvol=name
mount option. And this is what
the kernel argument above does.
That means that in order to add a new distro to multi-boot image, all we have
to do is:
- create a new BTRFS subvolume for it
- extract root filesystem of the distribution into it (see the
SYSTEM
file above, we extracted from LibreELEC.tv image)
- modify boot filesystem and add a new boot entry for the new distro, and
reference the new subvolume from it, so that our new boot option will boot into
the new subvolume
Let's step through that in detail. First we need to get access to the
multi-distro image somehow. This can be done from the phone itself, from the PC
via uSD card reader, or directly on the downloaded and image that was not
flashed yet. In all these cases we will want to have the image represented as a
partitioned block device. In all cases except the last one, this is already done
by the kernel automatically. To modify the unflashed image, you'd need to use
loop
device to turn the downloaded and extracted image into a block
device (for example via
losetup --find --show --partscan image.img
).
To find the multi-distro block devices, use
sudo blkid | grep 12345678
. For me this outputs (among other
things):
/dev/mmcblk2p2: UUID="dfe75f46-40a7-4593-902c-1659f6c05f2d" UUID_SUB="6b4a8f9f-d8d3-4f5b-9d66-6d3dcfe9e6c0" BLOCK_SIZE="4096" TYPE="btrfs" PARTUUID="12345678-02"
/dev/mmcblk2p1: PARTUUID="12345678-01"
(In my case I'm modifying an existing multi-distro installation on an eMMC
directly on Pinephone, so this found devices /dev/mmcblk2p1
with
the boot filesystem and /dev/mmcblk2p2
with the BTRFS. Names of
devices will vary depending on your situation.)
Now we will need to mount the BTRFS filesystem, and add LibreELEC.tv root
filesystem to it.
mkdir m
sudo mount /dev/mmcblk2p2 m
sudo subvolume create m/electv
And now extract the root filesystem (SYSTEM
file) to the new
subvolume (remember, it's in SquashFS format):
sudo unsquashfs -f -dest m/electv bootfs/SYSTEM
That's it for the root filesystem. We can unmount it now, and move on:
sudo umount m
Modifying the p-boot's boot
filesystem
Now we need to add a boot entry. p-boot uses a custom boot filesystem
format that is not understood by the Linux kernel. So we'll need some special
tools to modify it. The steps to modify the p-boot's bootfs are in general:
- extract bootfs contents from the partition to a directory
- modify the files in a directory
- re-create the bootfs on the partition from the modified files
Let's get the necessary tools first. I keep the latest versions statically
pre-compiled in
p-boot's repository. We will download the two tools needed to do our job
from there. You can also compile them yourself, if you're so inclined.
wget https://megous.com/git/p-boot/plain/dist/p-boot-conf
wget https://megous.com/git/p-boot/plain/dist/p-boot-unconf
chmod +x p-boot-{un,}conf
Now that we have all the tools, extract the boot filesystem (remember,
/dev/mmcblk2p1
is the p-boot's bootfs partition as we determined
earlier):
./p-boot-unconf pboot /dev/mmcblk2p1
tree pboot
Tree will show us the structure of files extracted from the boot
filesystem:
pboot
|-- atf-0.img
|-- boot.conf
|-- dtb-0.img
|-- dtb2-0.img
|-- files
| |-- arch-dreemurrs.argb
| |-- arch.argb
| |-- fedora.argb
| |-- jumpdrive.argb
| |-- lune.argb
| |-- maemo.argb
| |-- manjaro-phosh.argb
| |-- manjaro-plasma.argb
| |-- mobian.argb
| |-- neon.argb
| |-- off.argb
| |-- pboot.argb
| |-- pboot2.argb
| |-- pmos-dplasma.argb
| |-- pmos-fbkeyboard.argb
| |-- pmos-gnome.argb
| |-- pmos-mplasma.argb
| |-- pmos-phosh.argb
| |-- pureos.argb
| |-- sailfish.argb
| |-- sxmo.argb
| |-- ut.argb
| `-- xnux.argb
|-- initramfs-16.img
|-- linux-0.img
`-- splash
|-- splash-0.img
|-- splash-1.img
|-- splash-10.img
|-- splash-11.img
|-- splash-12.img
|-- splash-13.img
|-- splash-14.img
|-- splash-15.img
|-- splash-16.img
|-- splash-2.img
|-- splash-3.img
|-- splash-4.img
|-- splash-5.img
|-- splash-6.img
|-- splash-7.img
|-- splash-8.img
`-- splash-9.img
2 directories, 46 files
We're only interested in boot.conf
at this time. It contains the
configuration for all the boot options:
device_id = Distro Demo Image 2020-11-23
no = 0
name = Arch Linux ARM 2020-11-21
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=arch
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-0.img
no = 1
name = Arch Linux ARM / dreemurrs 20201112
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=arch-dreemurrs
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-1.img
no = 2
name = Lune OS 2020-11-23
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=lune bootmode=normal LUNEOS_NO_OUTPUT_REDIRECT
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-2.img
no = 3
name = Maemo Leste 20201101
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=maemo fbcon=rotate:1
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-3.img
no = 4
name = Manjaro / Phosh beta2-20201119
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=manjaro-phosh
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-4.img
no = 5
name = Manjaro / Plasma 201122
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=manjaro-plasma
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-5.img
no = 6
name = Mobian 20201121
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=mobian splash plymouth.ignore-serial-consoles vt.global_cursor_default=0
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-6.img
no = 7
name = KDE Neon 20201123-084050
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=neon splash
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-7.img
no = 8
name = pmOS / Plasma Desktop 2020-11-21
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=pmos-dplasma init=/sbin/init PMOS_NO_OUTPUT_REDIRECT
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-8.img
no = 9
name = pmOS / fbkeyboard 2020-11-21
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=pmos-fbkeyboard init=/sbin/init PMOS_NO_OUTPUT_REDIRECT
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-9.img
no = 10
name = pmOS / GNOME 2020-11-21
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=pmos-gnome init=/sbin/init PMOS_NO_OUTPUT_REDIRECT
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-10.img
no = 11
name = pmOS / Plasma Mobile 2020-11-21
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=pmos-mplasma init=/sbin/init PMOS_NO_OUTPUT_REDIRECT
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-11.img
no = 12
name = pmOS / Phosh 2020-11-21
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=pmos-phosh init=/sbin/init PMOS_NO_OUTPUT_REDIRECT
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-12.img
no = 13
name = Sailfish 1.1-3.3.0.16-devel-20201101
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=sailfish
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-13.img
no = 14
name = pmOS / sxmo nightly-202011090018
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=sxmo init=/sbin/init PMOS_NO_OUTPUT_REDIRECT
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-14.img
no = 15
name = Ubuntu Touch 2020-11-19
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 quiet loglevel=0 systemd.show_status=false cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=compress-force=zstd,nodatacow,subvol=ut logo.nologo vt.global_cursor_default=0
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
splash = splash/splash-15.img
no = 16
name = Jumpdrive 0.6
bootargs = loglevel=0 silent console=tty0 vt.global_cursor_default=0
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
initramfs = initramfs-16.img
splash = splash/splash-16.img
That's quite a lot of boot options! You can modify this list to your liking.
For me, I'll simply add a 17'th boot option for LibreELEC.tv:
cat <<EOF >> pboot/boot.conf
no = 17
name = LibreELEC.tv
bootargs = console=ttyS0,115200 earlycon=ns16550a,mmio32,0x01c28000 cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=subvol=electv
atf = atf-0.img
dtb = dtb-0.img
dtb2 = dtb2-0.img
linux = linux-0.img
EOF
I've removed all the kernel silencing options, because when playing with new
distro, we usually want all the debugging aids we can get. The most important
kernel argument is rootflags=subvol=electv
that one determines what
subvolume this p-boot menu option will use. Don't forget to change
no
option to an unique boot option index, either. In my case, this
is 17
.
To write the modifications to boot partition, we will run:
./p-boot-conf pboot /dev/mmcblk2p1
The tool will print a bynch of detailed information about what it did, and we
can now reboot to test the new distribution. :)
Boot testing
Here's a first video of the boot test:
Pretty good! :)
And it looks like LibreELEC.tv also has a touch interface:
The only problem is that Pinephone LCD panel has native resolution 720×1440,
which is not exactly great for watching videos, or using LibreELEC.tv's UI for
that matter (as you can see from the videos). One would have to figure out some
option for rotating the display.
Using LibreELEC.tv with
external monitor
Pinephone Convergence Dock to the rescue. :) LibreELEC.tv doesn't support
hotplugging the external monitor on Pinephone. It keeps displaying UI on the
primary LCD. We need to nudge it a bit, to do what we want. The easiest way to
do it is to disable the internal display using a kernel argument
(video=DSI-1:d
).
For that we can modify bootargs
line in boot.conf
and re-run p-boot-conf
exactly as before. We don't need to run
p-boot-unconf
anymore from now on, as long as we don't delete the
pboot
directory.
The final line will be:
bootargs = quiet loglevel=0 cma=256M console=tty1 consoleblank=0 panic=3 rw rootwait root=PARTUUID=12345678-02 rootfstype=btrfs rootflags=subvol=electv video=DSI-1:d
And let's try rebooting again, with dock connected to the phone and to the
external monitor:
Pretty good! :)
Now all I need to do is connect a keyboard/mouse and play with it a bit. It
looks quite snappy and the video playback should be accelerated on
Pinephone!
With p-boot, you can have your Pinephone serve as a mobile OS and TV box at
the same time. :) You can simply add LibreELEC.tv as one of the boot options,
and reboot your phone to it wherever you need to have a TV box. :)
It looks like LibreELEC.tv is comming soon to next multi-distro demo image,
after I do more testing! If you want to have it sooner than that. This article
shall be your guide. ;-) You can add any distro you like, this way.