Pine fun - Booting Genode on the PinePhone
Until now, my exploration of the Allwinner A64 SoC was mainly concerned with the Pine-A64-LTS board, which offers developer conveniences like booting over the network, or easily accessible reset and GPIO pins. Now, it's time to switch gears by moving the development workflow over to the PinePhone.
In the previous episode, we picked networking as a suitable topic to cover the principle steps needed for transplanting a moderately complex device driver from the Linux kernel to Genode. However, our vision of a Genode-based mobile phone requires drivers for device hardware that is not present on the Pine-A64-LTS board. I'm speaking of the display, touch screen, and modem, among many other peripherals. So the time is overdue to switch the development to the PinePhone hardware.
Booting from SD-card
Unlike regular smartphones, the PinePhone is able to boot directly from an SD card, which is the preferred way for trying out the various Linux-based operating systems that exist for the phone. Both the Pine-A64-LTS board as well as the PinePhone are supported by the official U-Boot boot loader. We already went though the process of manually building and installing U-Boot in an earlier article. In order to attain repeatable and robust development workflows, however, we have to automate these steps.
The first piece of the puzzle is an automated way of obtaining a known-to-work version of U-Boot, which comprises the downloading (and potentially patching) of the source code and its dependencies. In Genode, this step is covered by the so-called ports tool, which installs 3rd-party software according to a port description. In the case at hand, the port description file is ports/pine_uboot.port. The port consists of two parts that are mandatory for building U-Boot for the platform, namely U-Boot itself and the so-called ARM Trusted Firmware. Each of both parts is obtained from a separate Git repository.
The second part is the compile-time configuration and the actual build process of U-Boot. Here, Genode's build system comes in. The build target u-boot/pine/target.mk conserves all the magic spells needed to bake a U-Boot binary that is known to work well on the targeted board. From a Genode build directory, it can be invoked via:
build/arm_v8a$ make u-boot/pine BOARD=pinephone ... ... ... CONVERT SD-card image u-boot/pine/pinephone.img
Let me draw your attention to two interesting details of the target.mk file. It takes one of U-Boot's default configurations as starting point and merely tweaks a few options in a declarative way:
SUPPORTED_BOARDS := pine_a64lts pinephone UBOOT_DEFCONFIG(pine_a64lts) := pine64-lts_defconfig UBOOT_DEFCONFIG(pinephone) := pinephone_defconfig ... # read boot config from SD-card UBOOT_OPTION(pinephone) += ENV_EXT4_INTERFACE="mmc" UBOOT_OPTION(pinephone) += ENV_EXT4_DEVICE_AND_PART="0:1" UBOOT_ENABLE_COMMON += ENV_IS_IN_EXT4 CMD_EXT4_WRITE UBOOT_DISABLE_COMMON += ENV_IS_IN_FAT
Those declarations are taken as input into U-Boot's .config file by the rule $(UBOOT_CONFIG_FILE). In contrast to merely supplying a blessed .config file handcrafted once via make menuconfig, this approach nicely conserves the purpose behind configuration choices. E.g., as seen in the snippet above, we deliberately configure U-Boot for using EXT file-system support instead of FAT.
Another noteworthy detail is the $(SD_CARD_IMAGE_FILE) rule, which creates a ready-to-use SD-card image that hosts only the boot loader. The rule documents the (fairly non-obvious) steps needed for partitioning and formatting of an SD card image that works out of the box without any manual tweaking needed.
The third piece of the puzzle is the automated creation of a bootable SD-card image for any given Genode system scenario using Genode's run tool. The solution comes in the form of the tool/run/image/pine_uboot_sdcard plugin, which crafts an SD-card image containing U-Boot, a U-Boot runtime configuration, and a Genode system image. It can be activated by adding the following line to the build directory's etc/build.conf file:
RUN_OPT += --include image/pine_uboot_sdcard
When enabled, the execution of a run script results in a ready-to-use SD-card image that can be written to an SD-card using the dd command. For example:
build/arm_v8a$ make run/log KERNEL=hw BOARD=pine_a64lts ... Created SD-card image file var/run/log.img (6901kiB) build/arm_v8a$ sudo dd if=var/run/log.img of=/dev/sdx bs=1M conv=fsync
Of course /dev/sdx must be adjusted to the device of the SD-card.
Using fastboot with the PinePhone
A development workflow based on juggling SD-cards is hardly satisfying. It is also prone to wearing out the hardware. Since network boot is out of question due to the lack of a network connector on the phone, we long for an alternative way to load custom system images to the PinePhone. Here, the so-called fastboot mechanism enters the picture. If enabled in U-Boot, the boot loader enters a fastboot mode that uses the USB OTG connector for receiving a stream of bytes before issuing a boot command.
Using the u-boot/pine build target for BOARD=pinephone, the resulting SD-card image has everything needed for using this mechanism.
-
When booting this image, enter the boot prompt by hitting a key.
-
To automatically enter fastboot mode when booting up, one can define the bootcmd environment variable accordingly.
setenv bootcmd fastboot usb 0
-
The command executed after the data transfer is finished can be defined via the fastboot_bootcmd environment variable.
setenv fastboot_bootcmd bootm 0x42000800
The number 0x42000800 needs explanation. U-Boot's fastboot mechanism downloads the data to a buffer configured at compile time using the CONFIG_FASTBOOT_BUF_ADDR option. By default, the address is 0x42000000. It is shown by U-Boot's help fastboot command. But what's behind the offset 0x800? Fastboot wraps the transferred data in a so-called Android image. So the address 0x42000000 refers to the start of the Android image, which happens to have a header of 0x800 bytes. The actual content - the boot image we want to load - follows right after the header.
-
Furthermore, to shave off a few seconds from each test cycle, the customization of the bootdelay environment variable may be considered.
setenv bootdelay 0
-
Finally, one has to make the changed environment variables permanent by saving the changed environment back to the SD card.
saveenv
Upon the next boot, the PinePhone automatically enters the fastboot mode, waiting for the transfer of an image file over USB. At this point, the boot log happens to show the message "musb-hdrc: peripheral reset irq lost!".
At the host side, the following steps must be taken.
-
Enable fastboot loading in Genode's run tool by adding the following lines to the etc/build.conf file in the build directory.
RUN_OPT += --include image/uboot RUN_OPT += --include load/fastboot
-
Install the fastboot tools, e.g., the fastboot Debian package.
-
On Linux, we must allow a regular user (like us) to access the fastboot USB device. This can be achieved with a custom udev rule by adding a file /etc/udev/rules.d/99-pinephone-fastboot.rules with the following content:
SUBSYSTEM=="usb", ATTR{idVendor}=="1f3a", MODE="0666", GROUP="plugdev"
-
When developing inside a Linux VM in top of Sculpt OS, it is useful to add the following lines to /config/usb to ease the assignment of the PinePhone's UART and fastboot devices to the virtual machine.
<usb> ... <policy label_suffix="fastboot" vendor_id="0x1f3a" product_id="0x1010"/> <policy label_suffix="pinephoneuart" vendor_id="0x403" product_id="0x6001"/> </usb>
This way, one can refer to those devices in the /config/launcher/usb_devices_rom configuration, e.g., by adding the following two <device> nodes, VirtualBox is prompted to request the two USB sessions using the specified labels.
<inline> <device label="pinephoneart"/> <device label="fastboot"/> </inline>
Note that each reboot of the PinePhone results in a different fastboot USB device each time. In the current version of Sculpt, one needs to manually trigger the re-request of the "fastboot" USB session by removing and subsequently adding the corresponding <device> node manually.
With these steps taken, Genode scenarios can be sent directly to the PinePhone using fastboot. Executing the framebuffer test has become as simple as
build/arm_v8a$ make run/framebuffer_pinephone KERNEL=hw BOARD=pinephone
After rebooting the PinePhone and executing the command above, we are greeted with colorful patterns on the PinePhone's display and various messages of the framebuffer driver in Genode's log output.
Starting download of 1488896 bytes ........... downloading of 1488896 bytes finished ## Booting kernel from Legacy Image at 42000800 ... ... ... ... [init -> fb_drv] sun6i-mipi-dsi 1ca0000.dsi: Attached device xbd599 [init -> fb_drv] panel-sitronix-st7703 1ca0000.dsi.0: 720x1440@55 24bpp dsi 4dl - ready [init -> test-framebuffer] blue [init -> test-framebuffer] green [init -> test-framebuffer] red [init -> test-framebuffer] all colors mixed [init -> test-framebuffer] black & white stripes
Booting Linux using fastboot
To quickly cross-correlate the behaviour of Genode and Linux during the porting of device drivers, it's handy to use the fastboot mechanism for Linux too. In contrast to a Genode boot image, a Linux boot requires three pieces, namely a Linux kernel image, a device-tree binary (dtb), and an initial ramdisk (initrd). We can use a so-called U-Boot FIT image as a carrier for all three pieces combined using the following command.
build/arm_v8a$ ./u-boot/pine/pinephone/tools/mkimage \ -f auto -A arm64 -C none \ -d ./a64_linux/arch/arm64/boot/Image \ -b ./a64_linux/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dtb \ -i /path/to/build-busybox-aarch64/initrd \ -a 0x44000000 \ lx.itb
It is highly recommended to use the mkimage that comes along with our custom-built U-Boot to avoid subtle inconsistencies between the U-Boot version on the device and the tools installed on the host system.
The resulting lx.itb FIT image can be loaded to the PinePhone as soon as U-Boot has entered the fastboot mode.
build/arm_v8a$ fastboot boot lx.itb
To supply a custom Linux kernel command line, one can set U-Boot's bootargs environment variable. For reference, the following kernel command line enables early kernel output and debug messages.
setenv bootargs "earlyprintk earlycon debug rdinit=/bin/sh" saveenv
Thanks to Ivan Loskutov for the fantastic hints of how to get fastboot to work with the PinePhone! Thanks to Christian Helmuth and Johannes Schlatow for their assistance with figuring out how to fastboot FIT images!