Labels

Sunday 23 November 2014

Debian on Nexus 7: Booting Debian

In this post, I'm going to attempt to boot the Nexus 7 into a Debian environment. It won't be able to do very much, but it should at least boot up OK.

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/mmcblk0p30
So 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 f76c30b
and (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  system
I 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_WORKS
and 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.img
The -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.404s
And 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% /mnt
It 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-static
I 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.conf
which 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=wheezy
This 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 -a
Some 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 tty6
Next, 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       1
and created /etc/hostname with an appropriate hostname:
nexus-7
I 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.0
and 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 telnetd
All 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 simon
I 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 clean
The 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/proc
I 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 init
I 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/init
To 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 30
I 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.

Monday 10 November 2014

Debian on Nexus 7: Experimenting with the Screen

The minimal userspace ramdisk that I created in my previous post is a useful environment for a little experimentation with the Nexus 7. In this post, I'm going to see if I can get the tablet's screen to show something.

The Linux Framebuffer


In earlier versions of Android, it is possible to display a simple boot logo whilst the OS is booting. This boot logo is placed in the file /initlogo.rle within Android's ramdisk, and it is the job of Android's init to read and display that logo. The code that displays the logo appears to interact with a framebuffer device (/dev/graphics/fb0) in a perfectly standard way. So, it seems that Android still retains the Linux framebuffer. This method of displaying a boot logo has since been removed, however.

I can use the minimal userspace ramdisk to investigate this further. From the kernel's log:
~ # dmesg | grep -i framebuffer
[    0.603515] FrameBuffer[0] 1200x1920 size=28016640 bytes is registered successfully!
[    0.608612] FrameBuffer[1] 1920x1080 size=0 bytes is registered successfully!
and from BusyBox's fbset tool (which I retained in my BusyBox build configuration):
~ # fbset

mode "1200x1920-0"
        # D: 0.001 MHz, H: 0.001 kHz, V: 0.000 Hz
        geometry 1200 1920 1200 5760 32
        timings 1000000000 60 48 6 3 32 5
        accel false
        rgba 8/24,8/16,8/8,8/0
endmode
There is certainly framebuffer support here! Exploring sysfs, I find:
~ # ls /sys/class/graphics/fb0
bits_per_pixel    msm_fb_fps_level  stride
blank             msm_fb_type       subsystem
console           name              uevent
cursor            pan               virtual_size
dev               power             vsync_event
mode              rotate
modes             state
~ # cat /sys/class/graphics/fb0/name
msmfb44_90701
The 1200x1920 resolution matches the Nexus 7's own screen resolution, so fb0 probably drives the tablet's screen. The 1920x1080 full HD framebuffer might drive the display connected to the tablet's HDMI port. For now, I'll focus on fb0. As indicated above, the kernel driver providing framebuffer support is msm_fb (located under drivers/video/msm in the Android kernel tree I'm using), and this is enabled by CONFIG_FB_MSM in the kernel configuration.

The Framebuffer Console


If msm_fb acts as a normal framebuffer driver, then I should be able to get it to work with Linux's framebuffer console driver (fbcon). This is CONFIG_FRAMEBUFFER_CONSOLE in the kernel configuration. The first time I tried this, I included the framebuffer console driver as a built-in. Unfortunately, that appeared to cause the kernel to hang whilst booting (it didn't seem to be an outright panic, as I'd expect the tablet to reboot if that occurred). So, I had to include the console driver as a module instead.

I revisited my kernel tree in ~/Android/Nexus7-Debian/kernel/msm, re-entered the menuconfig configuration tool and set CONFIG_FRAMEBUFFER_CONSOLE=m to include the framebuffer console driver as a module. When I previously built the kernel, I also set CONFIG_ANDROID=n. If I hadn't, then I'd at the very least want to set CONFIG_ANDROID_RAM_CONSOLE=n, to avoid any confusion with consoles.

I then rebuilt the kernel, and installed the new module into the ramdisk filesystem, as follows:
$ ARCH=arm SUBARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make -j8
$ sudo bash -c "PATH=$PATH ARCH=arm SUBARCH=arm CROSS_COMPILE=arm-linux-gnueabi-" \
      "make modules_install INSTALL_MOD_PATH=../../initrd/rootfs/"
I also rebuilt the ramdisk, and the boot image:
$ cd ../../initrd/rootfs
$ 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
and deployed it to the tablet:
$ fastboot boot boot.img

Starting the Console


Next, I logged in with telnet and tried to load the framebuffer console driver:
$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.

nexus-7-initrd login: root
~ # modprobe fbcon
Success! Oh, wait a minute. Those are kernel stack traces! I can also read them from the telnet session with dmesg (after getting out of the modprobe with CTRL+C):
[   64.896728] BUG: scheduling while atomic: modprobe/172/0x00000002
[   64.896728] Modules linked in: fbcon(+) bitblit softcursor font
[   64.896728] [] (unwind_backtrace+0x0/0x11c) from [] (__schedule+0x78/0x628)
[   64.896759] [] (__schedule+0x78/0x628) from [] (schedule_timeout+0x294/0x34c)
[   64.896759] [] (schedule_timeout+0x294/0x34c) from [] (wait_for_common+0xd4/0x184)
[   64.896789] [] (wait_for_common+0xd4/0x184) from [] (msm_fb_pan_idle+0x48/0xa8)
[   64.896789] [] (msm_fb_pan_idle+0x48/0xa8) from [] (msm_fb_pan_display_ex+0x1a4/0x1d0)
[   64.896789] [] (msm_fb_pan_display_ex+0x1a4/0x1d0) from [] (msm_fb_pan_display+0x40/0x48)
[   64.896820] [] (msm_fb_pan_display+0x40/0x48) from [] (msm_fb_imageblit.part.18+0x5c/0x68)
[   64.896820] [] (msm_fb_imageblit.part.18+0x5c/0x68) from [] (bit_putcs+0x300/0x360 [bitblit])
[   64.896850] [] (bit_putcs+0x300/0x360 [bitblit]) from [] (fbcon_putcs+0xd4/0x118 [fbcon])
[   64.896850] [] (fbcon_putcs+0xd4/0x118 [fbcon]) from [] (fbcon_redraw.isra.9+0xdc/0x174 [fbcon])
[   64.896881] [] (fbcon_redraw.isra.9+0xdc/0x174 [fbcon]) from [] (fbcon_scroll+0x67c/0xc84 [fbcon])
[   64.896881] [] (fbcon_scroll+0x67c/0xc84 [fbcon]) from [] (scrup+0xb8/0xd4)
[   64.896911] [] (scrup+0xb8/0xd4) from [] (lf+0x28/0x68)
[   64.896911] [] (lf+0x28/0x68) from [] (vt_console_print+0x1c4/0x30c)
[   64.896911] [] (vt_console_print+0x1c4/0x30c) from [] (__call_console_drivers+0xb0/0xcc)
[   64.896942] [] (__call_console_drivers+0xb0/0xcc) from [] (console_unlock+0x150/0x240)
[   64.896942] [] (console_unlock+0x150/0x240) from [] (take_over_console+0x29c/0x2f4)
[   64.896972] [] (take_over_console+0x29c/0x2f4) from [] (fbcon_takeover+0x54/0xb4 [fbcon])
[   64.896972] [] (fbcon_takeover+0x54/0xb4 [fbcon]) from [] (fb_console_init+0x11c/0x14c [fbcon])
[   64.897003] [] (fb_console_init+0x11c/0x14c [fbcon]) from [] (do_one_initcall+0x90/0x160)
[   64.897003] [] (do_one_initcall+0x90/0x160) from [] (sys_init_module+0x18a0/0x1a68)
[   64.897033] [] (sys_init_module+0x18a0/0x1a68) from [] (ret_fast_syscall+0x0/0x30)
These are all bug checks (indicated by the "BUG:" prefix). The bit they seem to all have in common is:
(unwind_backtrace+0x0/0x11c) from [] (__schedule+0x78/0x628)
(__schedule+0x78/0x628) from [] (schedule_timeout+0x294/0x34c)
(schedule_timeout+0x294/0x34c) from [] (wait_for_common+0xd4/0x184)
(wait_for_common+0xd4/0x184) from [] (msm_fb_pan_idle+0x48/0xa8)
(msm_fb_pan_idle+0x48/0xa8) from [] (msm_fb_pan_display_ex+0x1a4/0x1d0)
(msm_fb_pan_display_ex+0x1a4/0x1d0) from [] (msm_fb_pan_display+0x40/0x48)
(msm_fb_pan_display+0x40/0x48) from [] (msm_fb_imageblit.part.18+0x5c/0x68)
(msm_fb_imageblit.part.18+0x5c/0x68) from [] (bit_putcs+0x300/0x360 [bitblit])
(bit_putcs+0x300/0x360 [bitblit]) from [] (fbcon_putcs+0xd4/0x118 [fbcon])
(fbcon_putcs+0xd4/0x118 [fbcon]) from [] (fbcon_redraw.isra.9+0xdc/0x174 [fbcon])
(fbcon_redraw.isra.9+0xdc/0x174 [fbcon]) from [] (fbcon_scroll+0x67c/0xc84 [fbcon])
(fbcon_scroll+0x67c/0xc84 [fbcon]) from [] (scrup+0xb8/0xd4)
(scrup+0xb8/0xd4) from [] (lf+0x28/0x68)
(lf+0x28/0x68) from [] (vt_console_print+0x1c4/0x30c)
(vt_console_print+0x1c4/0x30c) from [] (__call_console_drivers+0xb0/0xcc)
(__call_console_drivers+0xb0/0xcc) from [] (console_unlock+0x150/0x240)
(console_unlock+0x150/0x240) from [] (take_over_console+0x29c/0x2f4)
After a bit of research, this is what I think is going on. It appears that vt_console_print() (drivers/tty/vt/vt.c) is holding a spinlock whilst it carries out console operations. These operations call into fbcon and the msm_fb framebuffer driver, and this driver then requests a sleep. This is what the "scheduling while atomic" complaint is referring to. As detailed in section 5.5.2 of this page, a kernel thread may not sleep whilst it holds a spinlock. Unfortunately, the msm_fb framebuffer driver appears to heavily rely on the ability to sleep for periods of time. The function msm_fb_pan_idle() (drivers/video/msm/msm_fb.c) appears in all of these stack traces, and is called rather a lot in the msm_fb driver. The call that this function makes to wait_for_common() (through wait_for_completion_interruptible_timeout) results in the sleep which triggers the bug check.

It turns out that this problem has been seen before in other framebuffer drivers. Unfortunately, it looks like it would be a rather complex task to work around this in the msm_fb driver. Also, its hopefully unlikely that I'll run into the same problem when I try to get an X server to drive the framebuffer. So, whilst I can't get a framebuffer console right now, all is not lost!

Next Time...


... I'll make my first attempt at booting into an actual Debian environment.