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