Alpine on the Aopen Chromebase or Chromebox Mini with Mainline Kernel

From Alpine Linux
Revision as of 00:30, 13 April 2021 by Sodface (talk | contribs)

Introduction

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:

wget http://www.sodface.com/repo/Alpine-3.14-Linux-5.12-rc6-Aopen-Chromebase-Mini.tar.gz tar xzvf Alpine-3.14-Linux-5.12-rc6-Aopen-Chromebase-Mini.tar.gz sudo dd if=Alpine-3.14-Linux-5.12-rc6-Aopen-Chromebase-Mini.img of=/dev/[usb stick] bs=4096

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 confirm entering 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 chrombase-firmware.tar.gz /lib/firmware

Copy /proc/config.gz and the chrombase-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 two kernel cmdline files, one for USB and one for the onboard mmc:

echo "console=tty1 noinitrd nosplash root=/dev/sda2 rootfstype=ext4 rw rootwait" > cmdline-usb

echo "console=tty1 noinitrd nosplash root=/dev/mmcblk1p2 rootfstype=ext4 rw rootwait" > cmdline-mmc

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@1{ description = "kernel"; data = /incbin/("zImage"); type = "kernel_noload"; arch = "arm"; os = "linux"; compression = "none"; hash@1{ 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"; }; }; }; configurations { default = "conf@1"; conf@1{ kernel = "kernel@1"; fdt = "fdt@1"; }; }; };

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://git.kernel.org/torvalds/t/linux-5.12-rc5.tar.gz

Download the most 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 xzvf linux-5.12-rc5.tar.gz --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/
chrombase-firmware.tar.gz
chromeos/
chromeos-5.10.tar.gz
cmdline-mmc
cmdline-usb
config.gz
efi.patch
kernel-veyron.its
linux-5.12-rc5.tar.gz
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_DRM_MALI_DISPLAY ./scripts/config --enable CONFIG_ROCKCHIP_LVDS ./scripts/config --enable CONFIG_ROCKCHIP_RGB ./scripts/config --enable CONFIG_DRM_PANEL_LVDS ./scripts/config --enable CONFIG_VT ./scripts/config --enable CONFIG_FRAMEBUFFER_CONSOLE ./scripts/config --enable CONFIG_DRM_FBDEV_EMULATION

Make olddefconfig:

make CC=clang ARCH=arm -j$(grep -c processor /proc/cpuinfo) 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$(grep -c processor /proc/cpuinfo) zImage modules dtbs

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

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

cd to the root of the working directory and create the FIT image (ignore warnings about "no reg property"):

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

Create the signed kernel images kernel-usb.bin (for the USB stick) and kernel-mmc.bin (for the internal emmc):

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

The kernel-usb.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 cgpt dhcpcd e2fsprogs 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$(grep -c processor /proc/cpuinfo) modules_install

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

cd .. && sudo tar xzvf chrombase-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/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-usb.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 mnt/root/install-mmc.sh

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

Make it executable:

chmod +x mnt/root/install-mmc.sh

Copy the kernel-mmc.bin, mmc_type2.partmap, and install-mmc.sh files to root's home directory:

sudo cp kernel-mmc.bin 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.

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