Alpine on the Aopen Chromebase or Chromebox Mini with Mainline Kernel
This material is work-in-progress ... The instructions below have not been thoroughly tested and may break things. |
Introduction
Do not use this yet. I have to add some kernel patch steps, an internal emmc install script, edit some other things and provide links to pre-built images. 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. I will document the steps I took but will also provide links to files you can download so you can choose how much or how little you want to do yourself.
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.
The 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
Create or download the partition map file for the internal emmc:
Contents of mmc-type2.partmap
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:
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
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
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.
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