Openelec

From Attie's Wiki
Jump to: navigation, search

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 and SYSTEM 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...

NOTE: I later found that putting quotes around the 'boot' and 'disk' parmaeters let a bug with their scripts shine through. Without these quotes, it gets a bit further, but still doesn't boot all the way.

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 first one is part of the kernel mount / extract engine, and 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.

$ 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
$ cp arch/x86/boot/bzImage /openelec/system/KERNEL
Personal tools
Namespaces

Variants
Actions
Navigation
Toolbox