Openelec
I want an OpenELEC media PC for my TV. And I want it to boot over NFS...
Here goes!
Contents |
Get your hands on a release!
I picked the v1.95.5 build from http://openelec.tv/get-openelec.
Investigate the release
$ tar -xvf OpenELEC-Generic.i386-1.95.5.tar.bz2 OpenELEC-Generic.i386-1.95.5/ OpenELEC-Generic.i386-1.95.5/licenses/ OpenELEC-Generic.i386-1.95.5/licenses/LGPL2.txt OpenELEC-Generic.i386-1.95.5/licenses/GPL3.txt OpenELEC-Generic.i386-1.95.5/licenses/MIT_Modified.txt OpenELEC-Generic.i386-1.95.5/licenses/GPL.txt OpenELEC-Generic.i386-1.95.5/licenses/BSD_3_Clause.txt OpenELEC-Generic.i386-1.95.5/licenses/ATI.txt OpenELEC-Generic.i386-1.95.5/licenses/FDL1_2.txt OpenELEC-Generic.i386-1.95.5/licenses/MPL1_1.txt OpenELEC-Generic.i386-1.95.5/licenses/Artistic.txt OpenELEC-Generic.i386-1.95.5/licenses/LGPL3.txt OpenELEC-Generic.i386-1.95.5/licenses/Clarified_Artistic.txt OpenELEC-Generic.i386-1.95.5/licenses/Radeon_rlc.txt OpenELEC-Generic.i386-1.95.5/licenses/FDL.txt OpenELEC-Generic.i386-1.95.5/licenses/MIT.txt OpenELEC-Generic.i386-1.95.5/licenses/Public_Domain.txt OpenELEC-Generic.i386-1.95.5/licenses/GPL2.txt OpenELEC-Generic.i386-1.95.5/licenses/FDL1_3.txt OpenELEC-Generic.i386-1.95.5/licenses/Info-ZIP.txt OpenELEC-Generic.i386-1.95.5/licenses/NVIDIA.txt OpenELEC-Generic.i386-1.95.5/licenses/LGPL2_1.txt OpenELEC-Generic.i386-1.95.5/licenses/APSL.txt OpenELEC-Generic.i386-1.95.5/licenses/BSD_4_Clause.txt OpenELEC-Generic.i386-1.95.5/licenses/BSD_2_Clause.txt OpenELEC-Generic.i386-1.95.5/licenses/BSD.txt OpenELEC-Generic.i386-1.95.5/create_installstick OpenELEC-Generic.i386-1.95.5/create_virtualimage OpenELEC-Generic.i386-1.95.5/CHANGELOG OpenELEC-Generic.i386-1.95.5/openelec.ico OpenELEC-Generic.i386-1.95.5/README.md OpenELEC-Generic.i386-1.95.5/3rdparty/ OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/ OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/ OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/extlinux.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/isolinux.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/CodingStyle.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/mboot.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/logo/ OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/logo/syslinux-100.png OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/comboot.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/menu.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/rfc5071.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/pxelinux.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/gpt.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/syslinux.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/keytab-lilo.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/SubmittingPatches.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/memdisk.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/usbkey.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/distrib.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/doc/sdi.txt OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/win64/ OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/win64/syslinux64.exe OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/NEWS OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/dos/ OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/dos/syslinux.com OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/win32/ OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/win32/syslinux.exe OpenELEC-Generic.i386-1.95.5/3rdparty/syslinux/README OpenELEC-Generic.i386-1.95.5/3rdparty/md5sum/ OpenELEC-Generic.i386-1.95.5/3rdparty/md5sum/md5sum.exe OpenELEC-Generic.i386-1.95.5/3rdparty/md5sum/README OpenELEC-Generic.i386-1.95.5/RELEASE OpenELEC-Generic.i386-1.95.5/sample.conf/ OpenELEC-Generic.i386-1.95.5/sample.conf/extlinux.conf OpenELEC-Generic.i386-1.95.5/sample.conf/grub.conf OpenELEC-Generic.i386-1.95.5/sample.conf/syslinux.cfg OpenELEC-Generic.i386-1.95.5/sample.conf/syslinux_installer.cfg OpenELEC-Generic.i386-1.95.5/create_installstick.exe OpenELEC-Generic.i386-1.95.5/target/ OpenELEC-Generic.i386-1.95.5/target/SYSTEM OpenELEC-Generic.i386-1.95.5/target/KERNEL.md5 OpenELEC-Generic.i386-1.95.5/target/SYSTEM.md5 OpenELEC-Generic.i386-1.95.5/target/KERNEL OpenELEC-Generic.i386-1.95.5/create_installstick.bat OpenELEC-Generic.i386-1.95.5/INSTALL
The OpenELEC-Generic.i386-1.95.5/target/SYSTEM
and OpenELEC-Generic.i386-1.95.5/target/KERNEL
files are the most interesting here.
The rest is really support fluff...
Look inside the SYSTEM image
mkdir system && sudo mount -o loop ./SYSTEM ./system
/usr/bin/Xorg
is present, as is /usr/lib/xbmc/xbmc.bin
... good signs
Look inside the KERNEL image
$ file KERNEL KERNEL: Linux kernel x86 boot executable bzImage, version 3.2.21 (jetstream@1801-lin007) #1 SMP Fri Jul 6 01:46:03 CEST 2, RO-rootFS, swap_dev 0x4, Normal VGA $ grep -P -a -b --only-matching $'\xfd\x37\x7a\x58\x5a\x00' KERNEL 14946:?7zXZ 5133180:?7zXZ
Despite the fact that file
states that this is a bzImage, I don't believe it... There are two xz
magic headers, lets dig...
$ dd if=KERNEL bs=1 skip=14946 of=KERNEL.a.xz 5120734+0 records in 5120734+0 records out 5120734 bytes (5.1 MB) copied, 9.08123 s, 564 kB/s $ dd if=KERNEL bs=1 skip=5133180 of=KERNEL.b.xz 2500+0 records in 2500+0 records out 2500 bytes (2.5 kB) copied, 0.0062067 s, 403 kB/s $ file KERNEL.a.xz KERNEL.b.xz KERNEL.a.xz: XZ compressed data KERNEL.b.xz: XZ compressed data
These files will start with XZ compressed data, but when that ends, there is some other binary data. To decompress we need to use the --single-stream
argument to xz
so that it won't label the data corrupt because of this.
$ cat KERNEL.a.xz | xz -d --single-stream > KERNEL.a $ cat KERNEL.b.xz | xz -d --single-stream > KERNEL.b xz: (stdin): Compressed data is corrupt
Looks like the second Magic header was present for some other reason. Perhaps it's part of the decompression engine?
Now we have the uncompressed kernel, we also have the initramfs and the contents of /proc/config.gz
avaliable (inside KERNEL.a
).
$ grep -P -a -b --only-matching $'\x1f\x8b\x08\x00' KERNEL.a 5229342: 6605512: 10308181: $ for i in $(grep -P -a -b --only-matching $'\x1f\x8b\x08\x00' KERNEL.a | cut -d : -f 1); do dd if=KERNEL.a of=KERNEL.a.${i}.gz bs=1 skip=${i}; done 7551598+0 records in 7551598+0 records out 7551598 bytes (7.6 MB) copied, 14.7439 s, 512 kB/s 6175428+0 records in 6175428+0 records out 6175428 bytes (6.2 MB) copied, 11.0656 s, 558 kB/s 2472759+0 records in 2472759+0 records out 2472759 bytes (2.5 MB) copied, 4.82829 s, 512 kB/s $ file KERNEL.a.*.gz KERNEL.a.10308181.gz: gzip compressed data, ASCII, has comment, comment KERNEL.a.5229342.gz: gzip compressed data, ASCII, has CRC, was "", last modified: Tue May 7 14:40:32 2013, max speed KERNEL.a.6605512.gz: gzip compressed data, from Unix, max compression
Looking good... I wonder which is the kernel config? (it's KERNEL.a.6605512.gz
, you can delete the rest)
$ cat KERNEL.a.6605512.gz | gzip -d | less $ ln -s KERNEL.a.6605512.gz kernel.config.gz
And does it support "Root file system on NFS" / CONFIG_ROOT_NFS
(yes) and "IP: kernel level autoconfiguration" / CONFIG_IP_PNP
(yes)
CONFIG_ROOT_NFS=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y
Good news, at least we don't have to rebuild the kernel.
Give it a go!
In the following configuration:
- the NFS and DHCP server is
192.168.0.251
- the
KERNEL
andSYSTEM
files are stored at/openelec/system
- the
/openelec/storage
directory is ready for mounting as$HOME
on the thin client -
/openelec/system
is exported with the following settings:/openelec/system 192.168.0.0/24(ro,no_root_squash,no_subtree_check)
-
/openelec/storage
is exported with the following settings:/openelec/storage 192.168.0.0/24(rw,no_root_squash,no_subtree_check)
DEFAULT OpenElec PROMPT 0 LABEL OpenElec KERNEL KERNEL APPEND ip=dhcp boot="NFS=192.168.0.251:/openelec/system" disk="NFS=192.168.0.251:/openelec/storage" overlay ssh debugging
It doesn't work! If you press return and scroll off the bottom of the screen, then press <Shift>+<PgUp> until you reach the top again, you will see something like this:
*** Error in mount_disk : mount_part: Unknown filesystem "NFS=192.168.0.251:/openelec/system" *** ### Starting debugging shell... type exit to quit ###
Such fun...
Debug it
The first thing you'll want to do is see what the init script is doing. As the debugging shell has nothing but a very basic busybox, you'll need to have a look at the init script from your computer.
Extract the initramfs
Find the cpio magic for the initramfs (the kernel config tells us that it is uncompressed! - CONFIG_INITRAMFS_COMPRESSION_NONE=y
).
Don't forget we are looking for the 'newc' format (magic: 070701, not 070707).
$ grep -P -a -b --only-matching $'070701' KERNEL.a 7743402:070701 9204932:070701 9205044:070701 9205160:070701 9205304:070701 9205444:070701 9205564:070701 9205684:070701 9205800:070701 9205916:070701 9327016:070701 9643260:070701 9655752:070701 9655868:070701 9656000:070701 9656128:070701 9874084:070701 9874220:070701 10081532:070701 10243004:070701 10243120:070701 10243236:070701 10356532:070701 10356656:070701 10443284:070701 10475316:070701 10657148:070701 10657288:070701 10753544:070701 10753688:070701 10888016:070701 12528044:070701 12528184:070701 12622900:070701 12623016:070701 12623136:070701 12694464:070701 12694584:070701 12694700:070701
Here we can see that there are lots of CPIO archives... they are most probably concatenated one after the other, and will be overlaid when the kernel starts. The last one will be an empty archive...
$ cpio -o --format=newc < /dev/null | wc -c 1 block 512
A blank archive is 512 bytes, containing simply the TRAILER!!!
node, and padding up to the block boundary.
This means that the initramfs spans from byte 9204932
to 12694700+512
. The first one is going to be a 'search' value stored within the kernel source.
$ dd if=KERNEL.a of=KERNEL.a.9204932.cpio bs=1 skip=9204932 count=3490280 4951810+0 records in 4951810+0 records out 4951810 bytes (5.0 MB) copied, 9.23791 s, 536 kB/s $ ln -s KERNEL.a.9204932.cpio initramfs.cpio $ mkdir initramfs $ cd initramfs $ cat ../KERNEL.a.9204932.cpio | cpio -i 6817 blocks $ ls -l total 60 drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 bin drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 dev drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 etc drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 flash -rwxr-xr-x 1 attie attie 12374 Aug 6 22:50 init drwxr-xr-x 3 attie attie 4096 Aug 6 22:50 lib drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 proc drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 sbin drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 splash drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 storage drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 sys drwxr-xr-x 2 attie attie 4096 Aug 6 22:50 sysroot
And there is the init script!
Time for an NFS root
Extract the cpio archive somewhere useful, and add the following export: /openelec/initramfs 192.168.0.0/24(ro,no_root_squash,no_subtree_check)
.
Add the following to your kernel command line: root=/dev/nfs nfsroot=192.168.0.251:/openelec/initramfs init=/bin/sh
And build the kernel using the config we scrounged earlier (without the initramfs, flatten CONFIG_EXTRA_FIRMWARE
, and making sure you use the same version - v3.2.21)
$ git checkout -b openelec v3.2.21 $ cat ../kernel.config.gz | gzip -d > ./myconfig gzip: stdin: decompression OK, trailing garbage ignored $ cp myconfig .config $ ARCH=i386 make menuconfig # turn off CONFIG_BLK_DEV_INITRD # clear out CONFIG_EXTRA_FIRMWARE $ ARCH=i386 make bzImage