Alpine on the Aopen Chromebase or Chromebox Mini with Mainline Kernel

From Alpine Linux

Introduction

Note: This wiki was originally written for the Chromebase Mini (veyron-tiger). I've since acquired an Aopen Chromebox Mini (veyron-fievel) which is basically the same internally just without a touchscreen, so with a few small changes, this article and the resulting boot images can be used on both. I've updated just the steps needed to include support for both models.

This wiki article details the steps I took to get the mainline Linux kernel and Alpine Linux running on an Aopen Chromebase Mini. The Aopen Chromebase Mini is based on the Rockchip RK3288 32-bit ARM SOC and goes by the board name of veyron-tiger. The device is switched to developer mode and the stock bootloader is used to boot the mainline kernel.

To skip the guide and just download and write a boot image to a 500MB or larger USB stick (updated for Alpine 3.15.0 and Kernel 5.15.27):

wget http://www.sodface.com/repo/aopen-veyron/Alpine-3.15.0-Linux-5.15.27-Aopen-Veyron.tar.gz tar xzvf Alpine-3.15.0-Linux-5.15.27-Aopen-Veyron.tar.gz sudo dd if=Alpine-3.15.0-Linux-5.15.27-Aopen-Veyron.img of=/dev/[usb stick] bs=4096 sync

Prep the Build Machine

In my case, the kernel build will be done on an RPi4 running the armv7 build of Alpine Linux. The RPi4 is set up for building packages, though since we won't actually be building any Alpine packages, you can just install the necessary development packages noted below and skip much of the package wiki. See Creating an Alpine Package for help on setting up the build environment.

sudo apk add alpine-sdk sudo flex bison findutils sed bc diffutils linux-headers \ openssl-dev gmp-dev elfutils-dev mpc1-dev bash mpfr-dev perl installkernel \ xz uboot-tools vboot-utils dtc cgpt ncurses-dev clang e2fsprogs

I have successfully cross-compiled kernels and root filesystems in the past, but for this effort I chose to do everything natively on armv7. Next, create a working directory to use for the rest of this guide. Once you have your armv7 build machine setup, the next step is to gather some support files from the Chromebase and then start the kernel build.

Prep the Chromebase Mini

My Chromebase Mini was configured as a Google TV Meter device and I needed to get a standard ChromeOS image restored to it. Go to Google's recovery help page, scroll to the bottom and follow the steps to "Use a Linux Computer" to make the USB recovery media. Using a paperclip or similar tool, press and hold the recovery button on the right hand side of the Chromebase and then connect power. Release the recovery button when the recovery screen is displayed. Plug in the USB recovery media and follow the prompts to restore the image.

I don't know if there's a way around it or not, but I had to go through the initial setup and device registration steps using my personal google account before I was able to enable developer mode. After the registration steps are complete, remove power, plug in a keyboard, press and hold the recovery button, apply power and then at the "Chrome OS is missing or damaged", release the recovery button and press ctrl-d on the keyboard. Press the recovery button again to turn off OS verification and restart. At the OS verification screen, press ctrl-d again to transition to developer mode.
Once in developer mode you can log in or choose "browse as guest" to login and get a chrome browser window. Once there, open a crosh terminal window tab by pressing the ctrl+alt+t key combination. Then get to a root prompt:

crosh> shell chroneos@localhost / $ sudo su #

Enable USB boot:

crossystem dev_boot_usb=1

Generate the ChromeOS kernel config:

modprobe configs

Tar up /lib/firmware

tar czvf chromebase-firmware.tar.gz /lib/firmware

Copy /proc/config.gz and the chromebase-firmware.tar.gz to a working directory on the build machine.

Prep the Working Directory

Change to the working directory on the build machine. Create a dummy bootloader file:

dd if=/dev/zero of=bootloader.bin bs=512 count=1

Create a kernel command line file that will work for both usb and the internal mmc:

echo "console=tty1 noinitrd nosplash root=PARTUUID=%U/PARTNROFF=1 rootfstype=ext4 rw rootwait" > cmdline

Create the image source file kernel-veyron.its with the following contents:

Contents of kernel-veyron.its

/dts-v1/; / { description = "Chrome OS kernel image with one or more FDT blobs"; images { kernel-0{ description = "kernel"; data = /incbin/("zImage"); type = "kernel_noload"; arch = "arm"; os = "linux"; compression = "none"; hash-0{ algo = "sha1"; }; }; fdt-1{ description = "rk3288-veyron-tiger.dtb"; data = /incbin/("rk3288-veyron-tiger.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; hash-1{ algo = "sha1"; }; }; fdt-2{ description = "rk3288-veyron-fievel.dtb"; data = /incbin/("rk3288-veyron-fievel.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; hash-2{ algo = "sha1"; }; }; }; configurations { conf-0{ description = "Aopen Chromebase Mini"; kernel = "kernel-0"; fdt = "fdt-1"; }; conf-1{ description = "Aopen Chromebox Mini"; kernel = "kernel-0"; fdt = "fdt-2"; }; }; };

Create or download the partition map file for the internal emmc:

wget https://raw.githubusercontent.com/SolidHal/PrawnOS/master/filesystem/resources/partmaps/mmc_type2.partmap

Contents of mmc-type2.partmap

label: gpt label-id: EBA5A923-2F33-7C4E-AC9A-1555FD600D19 device: /dev/mmcblk2 unit: sectors first-lba: 34 last-lba: 30777310 /dev/mmcblk2p1 : start= 20480, size= 65536, type=FE3A2A5D-4F32-41A7-B725-ACCC3285A309, uuid=89B31CDB-1147-5241-8271-C1ADBB9BBB44, name="Kernel", attrs="GUID:49,51,52,54,56" /dev/mmcblk2p2 : start= 86016, size= 30691294, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=63DB8E49-63C4-984E-90A0-8AC3222C4771, name="Root"

Download the mainline linux kernel source:

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.12.tar.xz

Download a recent chromeos kernel source:

wget https://chromium.googlesource.com/chromiumos/third_party/kernel/+archive/refs/heads/merge/chromeos-5.10.tar.gz

Make a build directory and extract the mainline kernel source:

mkdir build && tar xJvf linux-5.12.tar.xz --strip-components=1 -C build/

Make a chromeos directory and extract the efi.c and efi.h files from the chromeos kernel source to it:

mkdir chromeos && tar xzvf chromeos-5.10.tar.gz -C chromeos/ block/partitions/efi.c block/partitions/efi.h

Create a patch file for efi.c and efi.h, this is needed so the partitions on the internal emmc are detected correctly:

diff -u build/block/partitions/efi.c chromeos/block/partitions/efi.c > efi.patch diff -u build/block/partitions/efi.h chromeos/block/partitions/efi.h >> efi.patch

The working directory should now contain the following files and directories:

bootloader.bin
build/
chromebase-firmware.tar.gz
chromeos/
chromeos-5.10.tar.gz
cmdline
config.gz
efi.patch
kernel-veyron.its
linux-5.12.tar.xz
mmc_type2.partmap

That's it for the prep, time to build the kernel.

Kernel Build

Extract config.gz into the build directory:

tar xzvf config.gz -C build/

cd into the build directory and rename config to .config:

cd build/ && mv config .config

Enable some additional config options:

./scripts/config --enable CONFIG_ROCKCHIP_PHY

Answer y when asked to overwrite .config then enable the rest of the options:

./scripts/config --enable CONFIG_ROCKCHIP_RGB ./scripts/config --enable CONFIG_ROCKCHIP_LVDS ./scripts/config --enable CONFIG_DRM_FBDEV_EMULATION ./scripts/config --enable CONFIG_DRM_MALI_DISPLAY ./scripts/config --enable CONFIG_DRM_PANEL_LVDS ./scripts/config --enable CONFIG_DRM_PANFROST ./scripts/config --enable CONFIG_FRAMEBUFFER_CONSOLE ./scripts/config --enable CONFIG_UEVENT_HELPER ./scripts/config --set-str CONFIG_UEVENT_HELPER_PATH "/sbin/hotplug" ./scripts/config --enable CONFIG_VT

Make olddefconfig:

make CC=clang ARCH=arm -j$(nproc) olddefconfig

Patch efi.c and efi.h:

patch -p1 < ../efi.patch

Compile the zImage, modules, and dtbs - this will take some time:

make CC=clang ARCH=arm -j$(nproc) zImage modules dtbs

Copy the zImage and tiger and fievel dtb files into the root of the working directory:

cp arch/arm/boot/zImage arch/arm/boot/dts/rk3288-veyron-tiger.dtb arch/arm/boot/dts/rk3288-veyron-fievel.dtb ..

cd to the root of the working directory and create the FIT image:

cd .. && mkimage -f kernel-veyron.its kernel-veyron.itb

Create the signed kernel image kernel.bin:

vbutil_kernel --pack kernel.bin --version 1 --arch arm \ --vmlinuz kernel-veyron.itb --bootloader bootloader.bin --config cmdline \ --keyblock /usr/share/vboot/devkeys/kernel.keyblock \ --signprivate /usr/share/vboot/devkeys/kernel_data_key.vbprivk

The kernel.bin file will be written to the boot partition of the USB stick later in the procedure.

Create Alpine Root Filesystem

Create a new directory in the working directory for the root filesystem:

sudo mkdir -p rootfs/etc/apk

Copy apk directories from the host system:

sudo cp -r /etc/apk/keys/ /etc/apk/repositories rootfs/etc/apk/

Initialize the apk package database and install a minimal package set:

sudo apk --initdb --arch armv7 --root ./rootfs add alpine-base \ kbd-bkeymaps chrony e2fsprogs haveged network-extras openssl openssh \ tzdata cgpt sfdisk wireless-tools wpa_supplicant

cd into the build directory and install the kernel modules to the new alpine rootfs:

cd build && sudo make CC=clang ARCH=arm INSTALL_MOD_PATH=$(pwd)/../rootfs/ -j$(nproc) modules_install

cd back to the root of the working dir and extract the firmware into the rootfs:

cd .. && sudo tar xzvf chromebase-firmware.tar.gz -C rootfs/

Have Alpine start some basic services on boot:

sudo ln -s /etc/init.d/devfs rootfs/etc/runlevels/sysinit/devfs sudo ln -s /etc/init.d/dmesg rootfs/etc/runlevels/sysinit/dmesg sudo ln -s /etc/init.d/mdev rootfs/etc/runlevels/sysinit/mdev sudo ln -s /etc/init.d/hwdrivers rootfs/etc/runlevels/sysinit/hwdrivers sudo ln -s /etc/init.d/modules rootfs/etc/runlevels/boot/modules sudo ln -s /etc/init.d/sysctl rootfs/etc/runlevels/boot/sysctl sudo ln -s /etc/init.d/hostname rootfs/etc/runlevels/boot/hostname sudo ln -s /etc/init.d/bootmisc rootfs/etc/runlevels/boot/bootmisc sudo ln -s /etc/init.d/swclock rootfs/etc/runlevels/boot/swclock sudo ln -s /etc/init.d/syslog rootfs/etc/runlevels/boot/syslog sudo ln -s /etc/init.d/mount-ro rootfs/etc/runlevels/shutdown/mount-ro sudo ln -s /etc/init.d/killprocs rootfs/etc/runlevels/shutdown/killprocs sudo ln -s /etc/init.d/savecache rootfs/etc/runlevels/shutdown/savecache sudo ln -s /etc/init.d/firstboot rootfs/etc/runlevels/default/firstboot

Tar up the rootfs:

sudo tar czvf alpine-rootfs.tar.gz -C rootfs/ .

Create the USB Boot Media

Insert a USB stick into the build machine, determine it's device path and substitute that path as applicable in the following steps.
Create a new gpt label on the USB stick:

echo ';' | sudo sfdisk --label gpt /dev/[usb stick]

Run cgpt create:

sudo cgpt create /dev/[usb stick]

Create the kernel and root filesystem partitions:

sudo cgpt add -i 1 -t kernel -b 8192 -s 32768 -l Kernel -S 1 -T 5 -P 10 /dev/[usb stick] sudo cgpt add -i 2 -t data -b 40960 -s `expr $(sudo cgpt show /dev/[usb stick] | grep 'Sec GPT table' | awk '{ print $1 }') - 40960` -l Root /dev/[usb stick]

Reread the partition table on the USB stick:

sudo partprobe /dev/[usb stick]

Write the kernel to the USB stick first partition:

sudo dd if=kernel.bin of=/dev/[usb stick first partition]

Create an ext4 filesystem on the USB stick second partition:

sudo mkfs.ext4 /dev/[usb stick second partition]

Create a directory and mount the USB stick second partition:

mkdir mnt && sudo mount /dev/[usb stick second partition] mnt/

Extract the root filesystem to the USB stick second partition:

sudo tar xzvf alpine-rootfs.tar.gz -C mnt/

Create a script file for installing to internal mmc:

Contents of install-mmc.sh

#!/bin/sh sfdisk /dev/mmcblk1 < mmc_type2.partmap || true dd if=/dev/sda1 of=/dev/mmcblk1p1 yes | mkfs.ext4 /dev/mmcblk1p2 mount /dev/mmcblk1p2 /mnt tar xzvf alpine-rootfs.tar.gz -C /mnt sync

Make it executable:

sudo chmod +x install-mmc.sh

Copy mmc_type2.partmap, install-mmc.sh, and alpine-rootfs.tar.gz to root's home directory:

sudo cp mmc_type2.partmap install-mmc.sh alpine-rootfs.tar.gz mnt/root/

Sync and unmount the second partition:

sync && sudo umount mnt/

Boot and Reload the Chromebase Mini

Remove the USB stick from your build machine and insert into the Chromebase Mini.
Connect a USB keyboard and power up the Chromebase.
At the OS verification screen press ctrl+u to boot from USB.
Login as root with no password.
Run the install script:

./install-mmc.sh

At this point we are done. Type reboot and at the OS verification screen, remove the usb stick and then press ctrl-d to boot from the internal flash.
If all went well you should be at a login prompt and can login as root with no password.
The rest of the OS configuration is the same as an any other Alpine install.

A Note on the Onboard Speakers

I found that the headphone jack works out of the box with alsa. I couldn't immediately figure out how to test the onboard speakers though and found the answer here: https://unix.stackexchange.com/questions/585278/how-to-enable-speakers-on-armv7-chromebook

I figured out how to enable the speakers on this device:

Near the far right-hand-side of the alsamixer channel listing are some channels labelled 'Left Speaker Mixer Left DAC' and 'Left Speaker Mixer Right DAC' (and similar channels for the Right Speaker). These have to be enabled and then the speaker gain can be controlled by the more obvious Speaker gain channels, as usual. These channels apparently enable/disable DACs for the speakers.

It seems there are quite a few channels that appear in the 'playback' filter of alsamixer that shouldn't be there (such as many channels relating to ADCs, which I assume are related to recording).

I also haven't been able to find any option for auto-muting, so it seems it is necessary to manually mute/unmute the speaker/headphone channels, as necessary.

A Note on Video Drivers

Starting with the most recent kernel build (5.13) I enabled CONFIG_DRM_MALI_DISPLAY, that change, along with the mesa-dri-gallium and mesa-egl packages, greatly improved video playback performance with mpv under X.

References

https://github.com/SolidHal/PrawnOS/blob/master/scripts/InstallScripts/InstallPrawnOS.sh
https://github.com/SolidHal/PrawnOS/blob/master/filesystem/resources/partmaps/mmc_type2.partmap
https://github.com/SolidHal/PrawnOS/issues/17
https://github.com/SolidHal/PrawnOS/commit/cad18910218414922b9a2777d4e997c3af2e84e4
https://gitlab.com/kalilinux/build-scripts/kali-arm/-/blob/master/chromebook-arm-veyron.sh
http://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices/cr-48-chrome-notebook-developer-information/how-to-boot-ubuntu-on-a-cr-48
https://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format
https://gist.github.com/kapilhp/01c3e8fc24d938eeaa45c1c2ab02eaaa
https://github.com/alpinelinux/alpine-chroot-install/blob/master/alpine-chroot-install
https://groups.google.com/a/chromium.org/g/chromium-os-reviews/c/mjpg4rNvaOA/m/Cc_N9gtQDQAJ
https://chromium.googlesource.com/chromiumos/third_party/kernel/+/refs/heads/chromeos-5.4/block/partitions/efi.c
https://unix.stackexchange.com/questions/401839/unable-to-mount-mmcblk-partition-on-linux
https://gitlab.alpinelinux.org/alpine/mkinitfs/-/blob/master/initramfs-init.in
https://unix.stackexchange.com/questions/585278/how-to-enable-speakers-on-armv7-chromebook
#11031