Labels

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.

No comments:

Post a Comment