The User Data Partition
As discussed in an earlier post, I've decided to use the tablet's user data partition (of 11GB or so in size) to hold the root filesystem. From Android (with adb shell), I can work out exactly what partition this is on the tablet's internal storage:
shell@flo:/data $ mount ... /dev/block/platform/msm_sdcc.1/by-name/system /system ext4 ... /dev/block/platform/msm_sdcc.1/by-name/cache /cache ext4 ... /dev/block/platform/msm_sdcc.1/by-name/userdata /data ext4 ... /dev/block/platform/msm_sdcc.1/by-name/persist /persist ext4 ... ... shell@flo:/data $ ls -l /dev/block/platform/msm_sdcc.1/by-name/userdata lrwxrwxrwx root root 2014-09-01 21:45 userdata -> /dev/block/mmcblk0p30So the user data partition is mmcblk0p30, under /dev/block in Android (this corresponds to /dev/mmcblk0p30 in my minimal ramdisk).
I already know that I need to use the make_ext4fs tool to build a sparse image file to package the root filesystem for flashing onto the Nexus 7 using fastboot. I fetched the AOSP sources for this tool as follows (using the commit points where the build worked for me):
$ cd ~/Android/Nexus7-Debian $ mkdir -p platform/{system,external} $ cd platform/system $ git clone https://android.googlesource.com/platform/system/core $ git clone https://android.googlesource.com/platform/system/extras $ cd core $ git reset --hard 39ab11d $ cd ../extras $ git reset --hard 5fb7c3e $ cd ../../external $ git clone https://android.googlesource.com/platform/external/libselinux $ cd libselinux $ git reset --hard f76c30band (after examining how the Android.mk files did things) built the tool with the following:
$ sudo apt-get install zlib1g-dev $ cd ~/Android/Nexus7-Debian/platform $ gcc -o make_ext4fs -Iexternal/libselinux/include -Isystem/core/include -Isystem/core/libsparse/include -DHOST \ system/extras/ext4_utils/{make_ext4fs.c,ext4fixup.c,ext4_utils.c,allocate.c,contents.c,extent.c,indirect.c,uuid.c,sha1.c,wipe.c,crc16.c,ext4_sb.c,make_ext4fs_main.c} \ external/libselinux/src/{callbacks.c,check_context.c,freecon.c,init.c,label.c,label_file.c,label_android_property.c} \ system/core/libsparse/{backed_block.c,output_file.c,sparse.c,sparse_crc32.c,sparse_err.c,sparse_read.c} -lz $ ls external make_ext4fs systemI then ensured that the make_ext4fs binary was copied to a suitable location on my path. Next, I need to try creating a test sparse image file to see if I can really flash the user data partition.
I'll quickly create a dummy root filesystem (with nothing particularly useful inside it):
$ mkdir /tmp/fstest $ touch /tmp/fstest/IT_WORKSand package it into a sparse image file:
$ cd ~/Android/Nexus7-Debian/out $ make_ext4fs -s -l 11770M fstest.img /tmp/fstest $ ls boot.img fstest.imgThe -s argument specifies that a sparse image should be produced, and the -l argument specifies the image size. The meaning of these arguments were deduced from platform/system/extras/ext4_utils/mkuserimg.sh in the AOSP. The value of 11,770MB was found by experimentation, comparing the result with the user data image file from the KTU84P Android factory image.
So, now I have my test user data image (alongside my minimal ramdisk boot image), I can try this out on the tablet. I can use fastboot to flash the user data image in the usual way:
$ fastboot flash userdata fstest.img erasing 'userdata'... OKAY [ 1.314s] sending 'userdata' (136890 KB)... OKAY [ 4.292s] writing 'userdata'... OKAY [ 5.797s] finished. total time: 11.404sAnd I'll boot and login to the minimal ramdisk as usual, and try mounting the user data partition:
$ fastboot boot boot.img downloading 'boot.img'... OKAY [ 0.256s] booting... OKAY [ 0.026s] finished. total time: 0.282s $ telnet 192.168.1.1 Trying 192.168.1.1... Connected to 192.168.1.1. Escape character is '^]'. nexus-7-initrd login: root ~ # mount /dev/mmcblk0p30 /mnt ~ # ls /mnt IT_WORKS lost+found ~ #Looks like it worked! Checking the partition sizes:
~ # df -h Filesystem Size Used Available Use% Mounted on none 894.7M 0 894.7M 0% /dev none 903.9M 0 903.9M 0% /tmp none 903.9M 4.0K 903.9M 0% /var /dev/mmcblk0p30 11.3G 129.4M 11.2G 1% /mntIt appears I only have 11.3GB for this image (as opposed to 11.9GB shown by Android for its user data partition), but this should be good enough. I'm now ready to create a Debian root filesystem.
Creating a Debian Filesystem
I'll go for a Debian 7 ("wheezy") filesystem, which I will build using multistrap (using the information from this page). This requires the following packages to be installed:
$ sudo apt-get install multistrap binfmt-support qemu qemu-user-staticI did find that I was hit by this bug, which I had to work around by removing the reference to $forceeyes on line 989 of /usr/sbin/multistrap before I could continue. I first created a multistrap configuration:
$ cd ~/Android/Nexus7-Debian $ mkdir debian $ cd debian $ gvim multistrap.confwhich I populated with the following:
[General] noauth=true unpack=true debootstrap=Wheezy aptsources=Wheezy arch=armel directory=./rootfs [Wheezy] packages=udev netbase net-tools ifupdown iputils-ping isc-dhcp-server isc-dhcp-client inetutils-inetd telnetd apt module-init-tools procps unzip sudo wget dialog libncurses5-dev vim source=http://ftp.uk.debian.org/debian/ keyring=debian-archive-keyring components=main contrib non-free suite=wheezyThis initial set of packages should be enough to replicate the functionality that I have with the minimal ramdisk (plus a couple of other things, like vim). I'll most likely need to add more packages to this initial set in time, to get enough functionality to allow the tablet to fetch subsequent packages itself over some network. Any dependency requirements that these packages have are automatically fulfilled. With my configuration ready, I kicked off multistrap with:
$ sudo multistrap -f ./multistrap.conf ... Multistrap system installed successfully in /home/simon/Android/Nexus7-Debian/debian/rootfs/.Now I have to configure the system appropriately before I can boot it with the tablet. To do this, I can enter a chroot shell and use qemu to perform transparent user emulation for any ARM binaries that I need to invoke:
$ sudo cp /usr/bin/qemu-arm-static rootfs/usr/bin $ sudo mount -t proc proc rootfs/proc $ sudo chroot ./rootfs /bin/bash I have no name!@my-pc:/#I can now perform the necessary post-install package setup routines:
I have no name!@my-pc:/# /var/lib/dpkg/info/dash.preinst install I have no name!@my-pc:/# dpkg --configure -aSome packages may complain about not being able to start things (because of the chroot environment), but this should be OK. Now to lay down the configuration. I first edited /etc/inittab and commented out the following lines (disabling getty invocations for virtual consoles which are not being used):
1:2345:respawn:/sbin/getty 38400 tty1 2:23:respawn:/sbin/getty 38400 tty2 3:23:respawn:/sbin/getty 38400 tty3 4:23:respawn:/sbin/getty 38400 tty4 5:23:respawn:/sbin/getty 38400 tty5 6:23:respawn:/sbin/getty 38400 tty6Next, I created /etc/fstab with the following contents:
# file system mount point type options dump pass proc /proc proc nodev,noexec,nosuid 0 0 sysfs /sys sysfs nodev,noexec,nosuid 0 0 /dev/mmcblk0p30 / ext4 errors=remount-ro 0 1and created /etc/hostname with an appropriate hostname:
nexus-7I then set a static IP address for the USB RNDIS network interface by adding the following to /etc/network/interfaces:
auto usb0 iface usb0 inet static address 192.168.1.1 netmask 255.255.255.0and configured the DHCP server to use this interface by setting the INTERFACES variable in /etc/default/isc-dhcp-server to:
INTERFACES="usb0"I also set an appropriate DHCP configuration by adding the following to /etc/dhcp/dhcpd.conf (as well as commenting out the enabled option lines):
subnet 192.168.1.0 netmask 255.255.255.0 { range 192.168.1.20 192.168.1.254; }To use the telnet daemon with inetd, I created /etc/inetd.conf with the following contents:
telnet stream tcp nowait root /usr/sbin/in.telnetd telnetdAll that remains is to set an appropriate root password, and add a normal user:
I have no name!@my-pc:/# passwd root I have no name!@my-pc:/# adduser simonI can also reduce the size of the initial image by 100MB or so, by clearing apt's cache:
I have no name!@my-pc:/# apt-get cleanThe system should now be ready, so I can leave the chroot environment and tidy up. On my system, syslogd appeared to have been started during the post-install process, so I had to stop that too (and clean up its /dev/xconsole FIFO pipe).
I have no name!@my-pc:/# pkill syslogd I have no name!@my-pc:/# rm -f /dev/xconsole I have no name!@my-pc:/# exit exit $ sudo umount rootfs/procI can now package this up into a sparse image, and deploy this to the tablet:
$ cd ~/Android/Nexus7-Debian/out $ sudo bash -c "PATH=$PATH make_ext4fs -s -l 11770M debian-rootfs.img ../debian/rootfs" $ fastboot flash userdata debian-rootfs.img
Reconfiguring the Ramdisk
I also need to make a minor change to my minimal ramdisk image, to allow it to boot Debian. This is accomplished by having it run a shell script as init, rather than using BusyBox's own init implementation. This shell script performs the necessary steps to mount the user data partition and transfer the boot process over to the init executable contained there. The script can also perform some tablet-specific tasks, such as enabling USB RNDIS.
$ cd ~/Android/Nexus7-Debian/initrd/rootfs $ sudo rm init $ sudo gvim initI created the following shell script to act as init (after seeing the example on this page):
#!/bin/sh # Mount core filesystems mount -t proc none /proc mount -t sysfs none /sys # Blink the white LED to show that we're booting echo 255 > /sys/class/leds/white/brightness echo 1 > /sys/class/leds/white/device/blink # Tickle the Android composite gadget driver into RNDIS mode echo 0 > /sys/class/android_usb/android0/enable echo 18d1 > /sys/class/android_usb/android0/idVendor echo 2d04 > /sys/class/android_usb/android0/idProduct echo 239 > /sys/class/android_usb/android0/bDeviceClass echo 2 > /sys/class/android_usb/android0/bDeviceSubClass echo 1 > /sys/class/android_usb/android0/bDeviceProtocol echo LGE > /sys/class/android_usb/android0/f_rndis/manufacturer echo 18D1 > /sys/class/android_usb/android0/f_rndis/vendorID echo 1 > /sys/class/android_usb/android0/f_rndis/wceis echo rndis > /sys/class/android_usb/android0/functions echo 1 > /sys/class/android_usb/android0/enable # Mount the real rootfs mount /dev/mmcblk0p30 /mnt # Stop blinking the white LED to show that we're ready echo 255 > /sys/class/leds/white/brightness echo 0 > /sys/class/leds/white/device/blink # Unmount core filesystems umount /proc umount /sys # Switch to the real rootfs exec switch_root /mnt /sbin/initTo keep this shell script minimal, I've avoided using devtmpfs. So the dev tree in the minimal ramdisk needs to contain the device nodes necessary to support this process. The only node that is missing is the block device node for the tablet's user data partition. I therefore created this additional node, alongside making the shell script executable:
$ sudo chmod 755 init $ sudo mknod -m 600 dev/mmcblk0p30 b 179 30I then rebuilt the ramdisk in the usual way:
$ sudo bash -c "find . | cpio -o -H newc | gzip > ../initrd-rootfs.cpio.gz" $ cd ../../out $ mkbootimg --base 0 --pagesize 2048 --kernel_offset 0x80208000 --ramdisk_offset 0x82200000 \ --second_offset 0x81100000 --tags_offset 0x80200100 \ --cmdline 'console=ttyHSL0,115200,n8 msm_rtb.filter=0x3F ehci-hcd.park=3' \ --kernel ../kernel/msm/arch/arm/boot/zImage \ --ramdisk ../initrd/initrd-rootfs.cpio.gz -o boot.img
Attempting the Boot
I can now try booting the tablet into Debian:
$ fastboot boot boot.img downloading 'boot.img'... OKAY [ 0.256s] booting... OKAY [ 0.026s] finished. total time: 0.282s $ telnet 192.168.1.1 Trying 192.168.1.1... Connected to 192.168.1.1. Escape character is '^]'. Debian GNU/Linux 7 nexus-7 login: simon Password: Linux nexus-7 3.4.0-g03485a6-dirty #2 SMP PREEMPT Tue Nov 4 21:32:26 GMT 2014 armv7l The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. simon@nexus-7:~$ mount /dev/mmcblk0p30 on / type ext4 (rw,relatime,data=ordered) tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=185120k,mode=755) tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k) proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) tmpfs on /dev type tmpfs (rw,relatime,size=10240k,mode=755) tmpfs on /run/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=370220k) devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620)Hurrah!! Debian is now running, from the user data partition! There's still a little way to go before it can be self-sufficient, but this is definitely awesome!
Next Time ...
... I'll see if I can get WiFi networking to work.