1.. _zephyr-audio-dsp-development-on-chromebooks: 2 3Zephyr Audio DSP Development on Chromebooks 4########################################### 5 6The Audio DSP on Intel Chromebooks is configured to use the SOF 7"Community" key for firmware signing, and can therefore accept 8arbitrary user-developed firmware like Zephyr applications (of which 9SOF is one), including the Zephyr samples and test suite. 10 11Initial TGL Chromebook Setup 12**************************** 13 14(These instructions were written specifically to the Asus Flip CX5 15device code named "delbin". But they should be reasonably applicable 16to any recent Intel device.) 17 18Power the device on and connect it to a wireless network. It will 19likely want to download a firmware update (mine did). Let this finish 20first, to ensure you have two working OS images. 21 22Enable Developer Mode 23===================== 24 25Power the device off (menu in lower right, or hold the power button 26on the side) 27 28Hold Esc + Refresh (the arrow-in-a-circle "reload" key above "3") and 29hit the power key to enter recovery mode. Note: the touchscreen and 30pad don't work in recovery mode, use the arrow keys to navigate. 31 32Select "Advanced Options", then "Enable Developer Mode" and confirm 33that you really mean it. Select "Boot from Internal Storage" at the 34bootloader screen. You will see this screen every time the machine 35boots now, telling you that the boot is unverified. 36 37Wait while the device does the required data wipe. My device takes 38about 15 minutes to completely write the stateful partition. On 39reboot, select "Boot from Internal Storage" again and set it up 40(again) with Google account. 41 42Make a Recovery Drive 43===================== 44 45You will at some point wreck your device and need a recovery stick. 46Install the Chromebook Recovery Utility from the Google Web Store and 47make one. 48 49You can actually do this on any machine (and any OS) with Chrome 50installed, but it's easiest on the Chromebook because it knows its 51device ID (for example "DELBIN-XHVI D4B-H4D-G4G-Q9A-A9P" for the Asus 52Tiger Lake board). Note that recovery, when it happens, will not 53affect developer mode or firmware settings but it **will wipe out the 54root filesystem and /usr/local customizations you have made**. So 55plan on a strategy that can tolerate data loss on the device you're 56messing with! 57 58Make the root filesystem writable 59================================= 60 61For security, ChromeOS signs and cryptographically verifies (using 62Linux's dm-verity feature) all access to the read-only root 63filesystem. Mucking with the rootfs (for example, to install modules 64for a custom kernel) requires that the dm-verity layer be turned off: 65 66First open a terminal with Ctrl-Alt-T. Then at the "crosh> " prompt 67issue the "shell" command to get a shell running as the "chronos" 68user. Finally (in developer mode) a simple "sudo su -" will get you a 69root prompt. 70 71.. code-block:: console 72 73 crosh> shell 74 chronos@localhost / $ sudo su - 75 localhost ~ # 76 77Now you need to turn of signature verification in the bootloader 78(because obviously we'll be breaking whatever signature existed). 79Note that signature verification is something done by the ROM 80bootloader, not the OS, and this setting is a (developer-mode-only) 81directive to that code: 82 83.. code-block:: console 84 85 cros# crossystem dev_boot_signed_only=0 86 87(*Note: for clarity, commands in this document entered at the ChromeOS 88core shell will be prefixed with a hostname of cros.*) 89 90Next you disable the validation step: 91 92.. code-block:: console 93 94 cros# /usr/share/vboot/bin/make_dev_ssd.sh --remove_rootfs_verification 95 96**THIS COMMAND WILL FAIL**, give you an error that you are changing 97the setting for the entire running system, and suggest an alternative 98"--partitions X" argument to use that modifies only the currently used 99partition. Run that modified command, then reboot. 100 101After rebooting, you will notice that your chromebook boots with the 102raw storage device (e.g. /dev/nvme0n1p5) mounted as root and not the 103"dm-0" verity device, and that the rootfs is read-write. 104 105Note: What this command actually does is modify the command line of 106the installed kernel image (it saves a backup in 107/mnt/stateful_partition/cros_sign_backups) so that it specifies 108"root=<guid>" and not "root=dm-0". It does seem to leave the other 109verity configuration in place though, it just doesn't try to mount the 110resulting (now-invalid!) partition. 111 112Metanote: The astute will note that we're probably going to throw this 113kernel out, and that we could probably have just edited the command 114line of the new kernel instead of flashing and rebooting into this 115modified one. But that's too many balls to juggle at once for me. 116 117Enable ChromeOS SSH 118=================== 119 120Once you are booted with a writable partition, you can turn on the 121built-in ssh server with: 122 123.. code-block:: console 124 125 cros# /usr/libexec/debugd/helpers/dev_features_ssh 126 127By default neither the "chronos" user nor root accounts have 128passwords, so unless you want to type a ssh key in by hand, you 129probably want to set a password for the first login (before you run 130ssh-copy-id, of course): 131 132.. code-block:: console 133 134 cros# passwd 135 136Now ssh into the chromebook and add your key to 137``.ssh/authorized_keys`` as you do for any Linux system. 138 139Install Crouton 140*************** 141 142The Zephyr integration tools require a proper Linux environment and 143won't run on ChromeOS's minimal distro. So we need to install a Linux 144personality. **DO NOT** bother installing the "Linux Development 145Environment" (Crostini) from the ChromeOS Developer settings. This 146personality runs inside a VM, where our tools need access to the real 147kernel running on the real hardware. Instead install Crouton 148(https://github.com/dnschneid/crouton), which is a community 149chroot-based personality that preserves access to the real hardware 150sysfs and /dev filesystem. These instructions install the "cli-extra" 151package list, there are X11-enabled ones available too if you prefer 152to work on the device screen directly. See the project page, etc... 153 154At a root shell, grab the installer and run it (note: /usr/local is 155the only writable filesystem without noexec, you must place the binary 156there for it to run!): 157 158.. code-block:: console 159 160 cros# mkdir -p /usr/local/bin 161 cros# curl -L https://github.com/dnschneid/crouton/raw/master/installer/crouton \ 162 > /usr/local/bin/crouton 163 cros# chmod 755 /usr/local/bin/crouton 164 cros# crouton -r focal -t cli-extra 165 166Start the Crouton chroot environment: 167 168.. code-block:: console 169 170 cros# startcli 171 172Now you are typing commands into the Ubuntu environment. Enable 173inbound ssh on Crouton, but on a port other than 22 (which is used for 174the native ChromeOS ssh server). I'm using 222 here (which is easy to 175remember, and not a registered port in /etc/services): 176 177.. code-block:: console 178 179 crouton# apt install iptables openssh-server 180 crouton# echo "Port 222" >> /etc/ssh/sshd_config 181 crouton# mkdir /run/sshd 182 crouton# iptables -I INPUT -p tcp --dport 222 -j ACCEPT 183 crouton# /usr/sbin/sshd 184 185(*As above: note that we have introduced a hostname of "crouton" to 186refer to the separate Linux personality.*) 187 188NOTE: the mkdir, iptables and sshd commands need to be run every time 189the chroot is restarted. You can put them in /etc/rc.local for 190convenience. Crouton doesn't run systemd (because it can't -- it 191doesn't own the system!) so Ubuntu services like openssh-server don't 192know how to start themselves. 193 194Building and Installing a Custom Kernel 195*************************************** 196 197On your build host, grab a copy of the ChromeOS kernel tree. The 198shipping device is using a 5.4 kernel, but the 5.10 tree works for me 199and seems to have been backporting upstream drivers such that its main 200hardware is all quite recent (5-6 weeks behind mainline or so). We 201place it in the home directory here for simplicity: 202 203.. code-block:: console 204 205 dev$ cd $HOME 206 dev$ git clone https://chromium.googlesource.com/chromiumos/third_party/kernel 207 dev$ cd kernel 208 dev$ git checkout chromeos-5.10 209 210(*Once again, we are typing into a different shell. We introduce the 211hostname "dev" here to represent the development machine on which you 212are building kernels and Zephyr apps. It is possible to do this on the 213chromebook directly, but not advisable. Remember the discussion above 214about requiring a drive wipe on system recovery!*) 215 216Note: you probably have an existing Linux tree somewhere already. If 217you do it's much faster to add this as a remote there and just fetch 218the deltas -- ChromeOS tracks upstream closely. 219 220Now you need a .config file. The Chromebook kernel ships with the 221"configs" module built which exposes this in the running kernel. You 222just have to load the module and read the file. 223 224.. code-block:: console 225 226 dev$ cd /path/to/kernel 227 dev$ ssh root@cros modprobe configs 228 dev$ ssh root@cros zcat /proc/config.gz > .config 229 230You will need to set some custom configuration variables differently 231from ChromeOS defaults (you can edit .config directly, or use 232menuconfig, etc...): 233 234+ ``CONFIG_HUGETLBFS=y`` - The Zephyr loader tool requires this 235+ ``CONFIG_EXTRA_FIRMWARE_DIR=n`` - This refers to a build directory 236 in Google's build environment that we will not have. 237+ ``CONFIG_SECURITY_LOADPIN=n`` - Pins modules such that they will 238 only load from one filesystem. Annoying restriction for custom 239 kernels. 240+ ``CONFIG_MODVERSIONS=n`` - Allow modules to be built and installed 241 from modified "dirty" build trees. 242 243Now build your kernel just as you would any other: 244 245.. code-block:: console 246 247 dev$ make olddefconfig # Or otherwise update .config 248 dev$ make bzImage modules # Probably want -j<whatever> for parallel build 249 250The modules you can copy directly to the (now writable) rootfs on the 251device. Note that this filesystem has very limited space (it's 252intended to be read only), so the INSTALL_MOD_STRIP=1 is absolutely 253required, and you may find you need to regularly prune modules from 254older kernels to make space: 255 256.. code-block:: console 257 258 dev$ make INSTALL_MOD_PATH=mods INSTALL_MOD_STRIP=1 modules_install 259 dev$ (cd mods/lib/modules; tar cf - .) | ssh root@cros '(cd /lib/modules; tar xfv -)' 260 261Pack and Install ChromeOS Kernel Image 262====================================== 263 264The kernel bzImage file itself needs to be signed and packaged into a 265ChromeOS vboot package and written directly to the kernel partition. 266Thankfully the tools to do this are shipped in Debian/Ubuntu 267repositories already: 268 269.. code-block:: console 270 271 $ sudo apt install vboot-utils vboot-kernel-utils 272 273Find the current kernel partition on the device. You can get this by 274comparing the "kernel_guid" command line parameter (passed by the 275bootloader) with the partition table of the boot drive, for example: 276 277.. code-block:: console 278 279 dev$ KPART=`ssh root@cros 'fdisk -l -o UUID,Device /dev/nvme0n1 | \ 280 grep -i $(sed "s/.*kern_guid=//" /proc/cmdline \ 281 | sed "s/ .*//") \ 282 | sed "s/.* //"'` 283 dev$ echo $KPART 284 /dev/nvme0n1p4 285 286Extract the command line from that image into a local file: 287 288.. code-block:: console 289 290 dev$ ssh root@cros vbutil_kernel --verify /dev/$KPART | tail -1 > cmdline.txt 291 292Now you can pack a new kernel image using the vboot tooling. Most of 293these arguments are boilerplate and always the same. The keys are 294there because the boot requires a valid signature, even though as 295configured it won't use it. Note the cannot-actually-be-empty dummy 296file passed as a "bootloader", which is a holdover from previous ROM 297variants which needed an EFI stub. 298 299.. code-block:: console 300 301 dev$ echo dummy > dummy.efi 302 dev$ vbutil_kernel --pack kernel.img --config cmdline.txt \ 303 --vmlinuz arch/x86_64/boot/bzImage \ 304 --keyblock /usr/share/vboot/devkeys/kernel.keyblock \ 305 --signprivate /usr/share/vboot/devkeys/kernel_data_key.vbprivk \ 306 --version 1 --bootloader dummy.efi --arch x86_64 307 308You can verify this image if you like with "vbutil_kernel --verify". 309 310Now just copy up the file and write it to the partition on the device: 311 312.. code-block:: console 313 314 $ scp kernel.img root@cros:/tmp 315 $ ssh root@cros dd if=/tmp/kernel.img of=/dev/nvme0n1p4 316 317Now reboot, and if all goes well you will find yourself running in 318your new kernel. 319 320Wifi Firmware Fixup 321=================== 322 323On the Tiger Lake Chromebook, the /lib/firmware tree is a bit stale 324relative to the current 5.10 kernel. The iwlwifi driver requests a 325firmware file that doesn't exist, leading to a device with no network. 326It's a simple problem, but a catastrophic drawback if uncorrected. It 327seems to be sufficient just to link the older version to the new name. 328(It would probably be better to copy the proper version from 329/lib/firmware from a recent kernel.org checkout.): 330 331.. code-block:: console 332 333 cros# cd /lib/firmware 334 cros# ln -s iwlwifi-QuZ-a0-hr-b0-62.ucode iwlwifi-QuZ-a0-hr-b0-64.ucode 335 336Build and Run a Zephyr Application 337********************************** 338 339Finally, with your new kernel booted, you are ready to run Zephyr 340code. 341 342Build rimage Signing Tool 343========================= 344 345First download and build a copy of the Sound Open Firmware "rimage" 346tool (these instructions put it in your home directory for clarity, 347but anywhere is acceptable): 348 349.. code-block:: console 350 351 dev$ cd $HOME 352 dev$ git clone https://github.com/thesofproject/rimage 353 dev$ cd rimage/ 354 dev$ git submodule init 355 dev$ git submodule update 356 dev$ cmake . 357 dev$ make 358 359Copy Integration Scripting to Chromebook 360======================================== 361 362There is a python scripts needed on the device, to be run inside 363the Crouton environment installed above. Copy them: 364 365.. code-block:: console 366 367 dev$ scp soc/intel/intel_adsp/tools/cavstool.py user@crouton: 368 369Then start the service in the Crouton environment: 370 371.. code-block:: console 372 373 crouton$ sudo ./cavstool.py user@crouton: 374 375 376Build and Sign Zephyr App 377========================= 378 379Zephyr applications build conventionally for this platform, and are 380signed with "west flash" with just a few extra arguments. Note that 381the key in use for the Tiger Lake DSP is the "3k" key from SOF, not 382the original that is used with older hardware. The output artifact is 383a "zephyr.ri" file to be copied to the device. 384 385.. code-block:: console 386 387 dev$ west build -b intel_adsp/cavs25 samples/hello_world 388 dev$ west sign --tool-data=~/rimage/config -t ~/rimage/rimage -- \ 389 -k $ZEPHYR_BASE/../modules/audio/sof/keys/otc_private_key_3k.pem 390 391Run it! 392======= 393 394The loader script takes the signed rimage file as its argument. Once 395it reports success, the application begins running immediately and its 396console output (in the SOF shared memory trace buffer) can be read by 397the logging script. 398 399.. code-block:: console 400 401 dev$ west flash --remote-host crouton 402 Hello World! intel_adsp 403 404Misc References 405*************** 406 407Upstream documentation from which these instructions were drawn: 408 409This page has the best reference for the boot process: 410 411https://www.chromium.org/chromium-os/developer-library/reference/device/disk-format 412 413This is great too, with an eye toward booting things other than ChromeOS: 414 415https://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices/custom-firmware 416