| :orphan: |
| |
| .. _zephyr-audio-dsp-development-on-chromebooks: |
| |
| Zephyr Audio DSP Development on Chromebooks |
| ########################################### |
| |
| The Audio DSP on Intel Chromebooks is configured to use the SOF |
| "Community" key for firmware signing, and can therefore accept |
| arbitrary user-developed firmware like Zephyr applications (of which |
| SOF is one), including the Zephyr samples and test suite. |
| |
| Initial TGL Chromebook Setup |
| **************************** |
| |
| (These instructions were written specifically to the Asus Flip CX5 |
| device code named "delbin". But they should be reasonably applicable |
| to any recent Intel device.) |
| |
| Power the device on and connect it to a wireless network. It will |
| likely want to download a firmware update (mine did). Let this finish |
| first, to ensure you have two working OS images. |
| |
| Enable Developer Mode |
| ===================== |
| |
| Power the device off (menu in lower right, or hold the power button |
| on the side) |
| |
| Hold Esc + Refresh (the arrow-in-a-circle "reload" key above "3") and |
| hit the power key to enter recovery mode. Note: the touchscreen and |
| pad don't work in recovery mode, use the arrow keys to navigate. |
| |
| Select "Advanced Options", then "Enable Developer Mode" and confirm |
| that you really mean it. Select "Boot from Internal Storage" at the |
| bootloader screen. You will see this screen every time the machine |
| boots now, telling you that the boot is unverified. |
| |
| Wait while the device does the required data wipe. My device takes |
| about 15 minutes to completely write the stateful partition. On |
| reboot, select "Boot from Internal Storage" again and set it up |
| (again) with Google account. |
| |
| Make a Recovery Drive |
| ===================== |
| |
| You will at some point wreck your device and need a recovery stick. |
| Install the Chromebook Recovery Utility from the Google Web Store and |
| make one. |
| |
| You can actually do this on any machine (and any OS) with Chrome |
| installed, but it's easiest on the Chromebook because it knows its |
| device ID (for example "DELBIN-XHVI D4B-H4D-G4G-Q9A-A9P" for the Asus |
| Tiger Lake board). Note that recovery, when it happens, will not |
| affect developer mode or firmware settings but it **will wipe out the |
| root filesystem and /usr/local customizations you have made**. So |
| plan on a strategy that can tolerate data loss on the device you're |
| messing with! |
| |
| Make the root filesystem writable |
| ================================= |
| |
| For security, ChromeOS signs and cryptographically verifies (using |
| Linux's dm-verity feature) all access to the read-only root |
| filesystem. Mucking with the rootfs (for example, to install modules |
| for a custom kernel) requires that the dm-verity layer be turned off: |
| |
| First open a terminal with Ctrl-Alt-T. Then at the "crosh> " prompt |
| issue the "shell" command to get a shell running as the "chronos" |
| user. Finally (in developer mode) a simple "sudo su -" will get you a |
| root prompt. |
| |
| .. code-block:: console |
| |
| crosh> shell |
| chronos@localhost / $ sudo su - |
| localhost ~ # |
| |
| Now you need to turn of signature verification in the bootloader |
| (because obviously we'll be breaking whatever signature existed). |
| Note that signature verification is something done by the ROM |
| bootloader, not the OS, and this setting is a (developer-mode-only) |
| directive to that code: |
| |
| .. code-block:: console |
| |
| cros# crossystem dev_boot_signed_only=0 |
| |
| (*Note: for clarity, commands in this document entered at the ChromeOS |
| core shell will be prefixed with a hostname of cros.*) |
| |
| Next you disable the validation step: |
| |
| .. code-block:: console |
| |
| cros# /usr/share/vboot/bin/make_dev_ssd.sh --remove_rootfs_verification |
| |
| **THIS COMMAND WILL FAIL**, give you an error that you are changing |
| the setting for the entire running system, and suggest an alternative |
| "--partitions X" argument to use that modifies only the currently used |
| partition. Run that modified command, then reboot. |
| |
| After rebooting, you will notice that your chromebook boots with the |
| raw storage device (e.g. /dev/nvme0n1p5) mounted as root and not the |
| "dm-0" verity device, and that the rootfs is read-write. |
| |
| Note: What this command actually does is modify the command line of |
| the installed kernel image (it saves a backup in |
| /mnt/stateful_partition/cros_sign_backups) so that it specifies |
| "root=<guid>" and not "root=dm-0". It does seem to leave the other |
| verity configuration in place though, it just doesn't try to mount the |
| resulting (now-invalid!) partition. |
| |
| Metanote: The astute will note that we're probably going to throw this |
| kernel out, and that we could probably have just edited the command |
| line of the new kernel instead of flashing and rebooting into this |
| modified one. But that's too many balls to juggle at once for me. |
| |
| Enable ChromeOS SSH |
| =================== |
| |
| Once you are booted with a writable partition, you can turn on the |
| built-in ssh server with: |
| |
| .. code-block:: console |
| |
| cros# /usr/libexec/debugd/helpers/dev_features_ssh |
| |
| By default neither the "chronos" user nor root accounts have |
| passwords, so unless you want to type a ssh key in by hand, you |
| probably want to set a password for the first login (before you run |
| ssh-copy-id, of course): |
| |
| .. code-block:: console |
| |
| cros# passwd |
| |
| Now ssh into the chromebook and add your key to |
| ``.ssh/authorized_keys`` as you do for any Linux system. |
| |
| Install Crouton |
| *************** |
| |
| The Zephyr integration tools require a proper Linux environment and |
| won't run on ChromeOS's minimal distro. So we need to install a Linux |
| personality. **DO NOT** bother installing the "Linux Development |
| Environment" (Crostini) from the ChromeOS Developer settings. This |
| personality runs inside a VM, where our tools need access to the real |
| kernel running on the real hardware. Instead install Crouton |
| (https://github.com/dnschneid/crouton), which is a community |
| chroot-based personality that preserves access to the real hardware |
| sysfs and /dev filesystem. These instructions install the "cli-extra" |
| package list, there are X11-enabled ones available too if you prefer |
| to work on the device screen directly. See the project page, etc... |
| |
| At a root shell, grab the installer and run it (note: /usr/local is |
| the only writable filesystem without noexec, you must place the binary |
| there for it to run!): |
| |
| .. code-block:: console |
| |
| cros# mkdir -p /usr/local/bin |
| cros# curl -L https://github.com/dnschneid/crouton/raw/master/installer/crouton \ |
| > /usr/local/bin/crouton |
| cros# chmod 755 /usr/local/bin/crouton |
| cros# crouton -r focal -t cli-extra |
| |
| Start the Crouton chroot environment: |
| |
| .. code-block:: console |
| |
| cros# startcli |
| |
| Now you are typing commands into the Ubuntu environment. Enable |
| inbound ssh on Crouton, but on a port other than 22 (which is used for |
| the native ChromeOS ssh server). I'm using 222 here (which is easy to |
| remember, and not a registered port in /etc/services): |
| |
| .. code-block:: console |
| |
| crouton# apt install iptables openssh-server |
| crouton# echo "Port 222" >> /etc/ssh/sshd_config |
| crouton# mkdir /run/sshd |
| crouton# iptables -I INPUT -p tcp --dport 222 -j ACCEPT |
| crouton# /usr/sbin/sshd |
| |
| (*As above: note that we have introduced a hostname of "crouton" to |
| refer to the separate Linux personality.*) |
| |
| NOTE: the mkdir, iptables and sshd commands need to be run every time |
| the chroot is restarted. You can put them in /etc/rc.local for |
| convenience. Crouton doesn't run systemd (because it can't -- it |
| doesn't own the system!) so Ubuntu services like openssh-server don't |
| know how to start themselves. |
| |
| Building and Installing a Custom Kernel |
| *************************************** |
| |
| On your build host, grab a copy of the ChromeOS kernel tree. The |
| shipping device is using a 5.4 kernel, but the 5.10 tree works for me |
| and seems to have been backporting upstream drivers such that its main |
| hardware is all quite recent (5-6 weeks behind mainline or so). We |
| place it in the home directory here for simplicity: |
| |
| .. code-block:: console |
| |
| dev$ cd $HOME |
| dev$ git clone https://chromium.googlesource.com/chromiumos/third_party/kernel |
| dev$ cd kernel |
| dev$ git checkout chromeos-5.10 |
| |
| (*Once again, we are typing into a different shell. We introduce the |
| hostname "dev" here to represent the development machine on which you |
| are building kernels and Zephyr apps. It is possible to do this on the |
| chromebook directly, but not advisable. Remember the discussion above |
| about requiring a drive wipe on system recovery!*) |
| |
| Note: you probably have an existing Linux tree somewhere already. If |
| you do it's much faster to add this as a remote there and just fetch |
| the deltas -- ChromeOS tracks upstream closely. |
| |
| Now you need a .config file. The Chromebook kernel ships with the |
| "configs" module built which exposes this in the running kernel. You |
| just have to load the module and read the file. |
| |
| .. code-block:: console |
| |
| dev$ cd /path/to/kernel |
| dev$ ssh root@cros modprobe configs |
| dev$ ssh root@cros zcat /proc/config.gz > .config |
| |
| You will need to set some custom configuration variables differently |
| from ChromeOS defaults (you can edit .config directly, or use |
| menuconfig, etc...): |
| |
| + ``CONFIG_HUGETLBFS=y`` - The Zephyr loader tool requires this |
| + ``CONFIG_EXTRA_FIRMWARE_DIR=n`` - This refers to a build directory |
| in Google's build environment that we will not have. |
| + ``CONFIG_SECURITY_LOADPIN=n`` - Pins modules such that they will |
| only load from one filesystem. Annoying restriction for custom |
| kernels. |
| + ``CONFIG_MODVERSIONS=n`` - Allow modules to be built and installed |
| from modified "dirty" build trees. |
| |
| Now build your kernel just as you would any other: |
| |
| .. code-block:: console |
| |
| dev$ make olddefconfig # Or otherwise update .config |
| dev$ make bzImage modules # Probably want -j<whatever> for parallel build |
| |
| The modules you can copy directly to the (now writable) rootfs on the |
| device. Note that this filesystem has very limited space (it's |
| intended to be read only), so the INSTALL_MOD_STRIP=1 is absolutely |
| required, and you may find you need to regularly prune modules from |
| older kernels to make space: |
| |
| .. code-block:: console |
| |
| dev$ make INSTALL_MOD_PATH=mods INSTALL_MOD_STRIP=1 modules_install |
| dev$ (cd mods/lib/modules; tar cf - .) | ssh root@cros '(cd /lib/modules; tar xfv -)' |
| |
| Pack and Install ChromeOS Kernel Image |
| ====================================== |
| |
| The kernel bzImage file itself needs to be signed and packaged into a |
| ChromeOS vboot package and written directly to the kernel partition. |
| Thankfully the tools to do this are shipped in Debian/Ubuntu |
| repositories already: |
| |
| .. code-block:: console |
| |
| $ sudo apt install vboot-utils vboot-kernel-utils |
| |
| Find the current kernel partition on the device. You can get this by |
| comparing the "kernel_guid" command line parameter (passed by the |
| bootloader) with the partition table of the boot drive, for example: |
| |
| .. code-block:: console |
| |
| dev$ KPART=`ssh root@cros 'fdisk -l -o UUID,Device /dev/nvme0n1 | \ |
| grep -i $(sed "s/.*kern_guid=//" /proc/cmdline \ |
| | sed "s/ .*//") \ |
| | sed "s/.* //"'` |
| dev$ echo $KPART |
| /dev/nvme0n1p4 |
| |
| Extract the command line from that image into a local file: |
| |
| .. code-block:: console |
| |
| dev$ ssh root@cros vbutil_kernel --verify /dev/$KPART | tail -1 > cmdline.txt |
| |
| Now you can pack a new kernel image using the vboot tooling. Most of |
| these arguments are boilerplate and always the same. The keys are |
| there because the boot requires a valid signature, even though as |
| configured it won't use it. Note the cannot-actually-be-empty dummy |
| file passed as a "bootloader", which is a holdover from previous ROM |
| variants which needed an EFI stub. |
| |
| .. code-block:: console |
| |
| dev$ echo dummy > dummy.efi |
| dev$ vbutil_kernel --pack kernel.img --config cmdline.txt \ |
| --vmlinuz arch/x86_64/boot/bzImage \ |
| --keyblock /usr/share/vboot/devkeys/kernel.keyblock \ |
| --signprivate /usr/share/vboot/devkeys/kernel_data_key.vbprivk \ |
| --version 1 --bootloader dummy.efi --arch x86_64 |
| |
| You can verify this image if you like with "vbutil_kernel --verify". |
| |
| Now just copy up the file and write it to the partition on the device: |
| |
| .. code-block:: console |
| |
| $ scp kernel.img root@cros:/tmp |
| $ ssh root@cros dd if=/tmp/kernel.img of=/dev/nvme0n1p4 |
| |
| Now reboot, and if all goes well you will find yourself running in |
| your new kernel. |
| |
| Wifi Firmware Fixup |
| =================== |
| |
| On the Tiger Lake Chromebook, the /lib/firmware tree is a bit stale |
| relative to the current 5.10 kernel. The iwlwifi driver requests a |
| firmware file that doesn't exist, leading to a device with no network. |
| It's a simple problem, but a catastrophic drawback if uncorrected. It |
| seems to be sufficient just to link the older version to the new name. |
| (It would probably be better to copy the proper version from |
| /lib/firmware from a recent kernel.org checkout.): |
| |
| .. code-block:: console |
| |
| cros# cd /lib/firmware |
| cros# ln -s iwlwifi-QuZ-a0-hr-b0-62.ucode iwlwifi-QuZ-a0-hr-b0-64.ucode |
| |
| Build and Run a Zephyr Application |
| ********************************** |
| |
| Finally, with your new kernel booted, you are ready to run Zephyr |
| code. |
| |
| Build rimage Signing Tool |
| ========================= |
| |
| First download and build a copy of the Sound Open Firmware "rimage" |
| tool (these instructions put it in your home directory for clarity, |
| but anywhere is acceptable): |
| |
| .. code-block:: console |
| |
| dev$ cd $HOME |
| dev$ git clone https://github.com/thesofproject/rimage |
| dev$ cd rimage/ |
| dev$ git submodule init |
| dev$ git submodule update |
| dev$ cmake . |
| dev$ make |
| |
| Copy Integration Scripting to Chromebook |
| ======================================== |
| |
| There is a python scripts needed on the device, to be run inside |
| the Crouton environment installed above. Copy them: |
| |
| .. code-block:: console |
| |
| dev$ scp soc/xtensa/intel_adsp/tools/cavstool.py user@crouton: |
| |
| Then start the service in the Crouton environment: |
| |
| .. code-block:: console |
| |
| crouton$ sudo ./cavstool.py user@crouton: |
| |
| |
| Build and Sign Zephyr App |
| ========================= |
| |
| Zephyr applications build conventionally for this platform, and are |
| signed with "west flash" with just a few extra arguments. Note that |
| the key in use for the Tiger Lake DSP is the "3k" key from SOF, not |
| the original that is used with older hardware. The output artifact is |
| a "zephyr.ri" file to be copied to the device. |
| |
| .. code-block:: console |
| |
| dev$ west build -b intel_adsp_cavs25 samples/hello_world |
| dev$ west sign --tool-data=~/rimage/config -t ~/rimage/rimage -- \ |
| -k $ZEPHYR_BASE/../modules/audio/sof/keys/otc_private_key_3k.pem |
| |
| Run it! |
| ======= |
| |
| The loader script takes the signed rimage file as its argument. Once |
| it reports success, the application begins running immediately and its |
| console output (in the SOF shared memory trace buffer) can be read by |
| the logging script. |
| |
| .. code-block:: console |
| |
| dev$ west flash --remote-host crouton |
| Hello World! intel_adsp_cavs25 |
| |
| Misc References |
| *************** |
| |
| Upstream documentation from which these instructions were drawn: |
| |
| This page has the best reference for the boot process: |
| |
| http://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format |
| |
| This is great too, with an eye toward booting things other than ChromeOS: |
| |
| https://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices/custom-firmware |