<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>It's Pi all the way down... - Dave Jones</title><link href="https://waldorf.waveform.org.uk/" rel="alternate"></link><link href="https://waldorf.waveform.org.uk/feeds/dave-jones.atom.xml" rel="self"></link><id>https://waldorf.waveform.org.uk/</id><updated>2025-07-11T11:58:36+01:00</updated><entry><title>Pull yourself up by your bootstraps</title><link href="https://waldorf.waveform.org.uk/2025/pull-yourself-up-by-your-bootstraps.html" rel="alternate"></link><published>2025-07-09T00:00:00+01:00</published><updated>2025-07-10T21:05:08+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2025-07-09:/2025/pull-yourself-up-by-your-bootstraps.html</id><summary type="html">&lt;p class="first last"&gt;Some fairly radical changes to the way Ubuntu boots on the Raspberry&amp;nbsp;Pi&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;Updated&lt;/strong&gt;: Minor punctuation changes, and a small clarification of why the
current fallback probably isn&amp;#8217;t a fallback at&amp;nbsp;all&lt;/p&gt;
&lt;p&gt;The way Ubuntu boots on the Raspberry is changing in questing. Here&amp;#8217;s the story
behind the changes (along with my usual copious tangents, and finishing up with
some details on how to avoid these changes if you really, &lt;em&gt;really&lt;/em&gt; need&amp;nbsp;to).&lt;/p&gt;
&lt;div class="section" id="boots-an-all"&gt;
&lt;h2&gt;Boots an&amp;#8217;&amp;nbsp;all&lt;/h2&gt;
&lt;p&gt;Our current boot setup is… far from optimal. To understand it we should first
have a look at how the Pi boots, and how Ubuntu uses this (skip this section if
you&amp;#8217;re familiar with the Pi&amp;#8217;s&amp;nbsp;bootloader!).&lt;/p&gt;
&lt;p&gt;The Pi&amp;#8217;s native bootloader is split into three parts which we&amp;#8217;ll simply call
Stages 1, 2, and&amp;nbsp;3.&lt;/p&gt;
&lt;p&gt;Stage 1 is always on the SoC (on all models). It&amp;#8217;s main job (as far as we&amp;#8217;re
concerned) as to find and load stage 2. Its configuration is the
&lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt; file. If this is present, it must be on the first partition of
the boot media. Due to stage 1&amp;#8217;s very limited resources (there&amp;#8217;s no &lt;span class="caps"&gt;RAM&lt;/span&gt;
available at this stage), this file is limited to 512 bytes, and only
understands a few directives. The most important is &lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt; which
tells it which partition contains the rest of the boot&amp;nbsp;media.&lt;/p&gt;
&lt;p&gt;Prior to the Pi 4, stage 2 was the &lt;tt class="docutils literal"&gt;bootcode.bin&lt;/tt&gt; executable. From the Pi 4
onwards, this is part of the boot &lt;span class="caps"&gt;EEPROM&lt;/span&gt;. I&amp;#8217;m not fully clear on all the things
stage 2 does, but I do know this stage handles bringing up some more bits of
hardware to load and execute the (much bigger) third stage. For instance, when
network booting, it&amp;#8217;s this stage that brings the ethernet port up, performs
&lt;span class="caps"&gt;DHCP&lt;/span&gt;, and starts the &lt;span class="caps"&gt;TFTP&lt;/span&gt; process for the other boot&amp;nbsp;assets.&lt;/p&gt;
&lt;p&gt;The second stage also reads at least &lt;em&gt;some&lt;/em&gt; of the &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; configuration
file, as this can be used to customize which binary is loaded for the third&amp;nbsp;stage.&lt;/p&gt;
&lt;p&gt;Stage 3 is the real &amp;#8220;meat&amp;#8221; of the bootloader. On the Pi 3 and earlier it was
named &lt;tt class="docutils literal"&gt;start.elf&lt;/tt&gt;; on the Pi 4 this became &lt;tt class="docutils literal"&gt;start4.elf&lt;/tt&gt; file; on the Pi 5
this is another part of the boot &lt;span class="caps"&gt;EEPROM&lt;/span&gt; &lt;a class="footnote-reference" href="#kernel-only" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. There were 4
historical variants of this&amp;nbsp;stage:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;start.elf /&amp;nbsp;start4.elf&lt;/dt&gt;
&lt;dd&gt;The default third&amp;nbsp;stage&lt;/dd&gt;
&lt;dt&gt;start_cd.elf /&amp;nbsp;start4cd.elf&lt;/dt&gt;
&lt;dd&gt;A &amp;#8220;cut down&amp;#8221; version of the bootloader with minimal facilities &lt;a class="footnote-reference" href="#cut-down" id="footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/dd&gt;
&lt;dt&gt;start_db.elf /&amp;nbsp;start4db.elf&lt;/dt&gt;
&lt;dd&gt;The &amp;#8220;debug&amp;#8221; build of the&amp;nbsp;bootloader&lt;/dd&gt;
&lt;dt&gt;start_x.elf /&amp;nbsp;start4x.elf&lt;/dt&gt;
&lt;dd&gt;The bootloader incorporating the legacy camera firmware &lt;a class="footnote-reference" href="#gpu" id="footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Each of these binaries has a corresponding &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;fixup*.dat&lt;/span&gt;&lt;/tt&gt; (&lt;tt class="docutils literal"&gt;fixup.dat&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;fixup4.dat&lt;/tt&gt;, etc.) file which contains the relocation information &lt;a class="footnote-reference" href="#afaik" id="footnote-reference-4"&gt;[5]&lt;/a&gt;
for each&amp;nbsp;binary.&lt;/p&gt;
&lt;p&gt;This stage is responsible for loading &lt;a class="footnote-reference" href="#maybe-dt" id="footnote-reference-5"&gt;[4]&lt;/a&gt; the device-tree and
customizing it for the hardware detected, loading the binaries containing the
Linux kernel, and (optionally) the initramfs. It parses all of &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;
which has many options that can be used to customize the booted state. It also
reads &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; which contains the Linux kernel&amp;#8217;s command line.
Importantly, this also defines the eventual rootfs&amp;nbsp;location.&lt;/p&gt;
&lt;p&gt;Finally, this stage is responsible for bringing the &lt;span class="caps"&gt;ARM&lt;/span&gt; cores online and
starting the kernel (passing it the addresses of the fixed up device-tree, the
optional &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Initial_ramdisk"&gt;initramfs&lt;/a&gt;, and the kernel command&amp;nbsp;line).&lt;/p&gt;
&lt;p&gt;Summarizing, the following table shows the location of the various stages on
the various generations of&amp;nbsp;Pi:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="42%" /&gt;
&lt;col width="10%" /&gt;
&lt;col width="15%" /&gt;
&lt;col width="33%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Generation&lt;/th&gt;
&lt;th class="head"&gt;SoC&lt;/th&gt;
&lt;th class="head"&gt;&lt;span class="caps"&gt;EEPROM&lt;/span&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;span class="caps"&gt;SD&lt;/span&gt; / &lt;span class="caps"&gt;USB&lt;/span&gt; / NVMe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Pi 1, 2, 3, and Zero&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pi 4, 400&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pi 5, 500&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2, 3&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;One other important thing to note is that the various stages can &lt;em&gt;only&lt;/em&gt; read
the &lt;span class="caps"&gt;FAT&lt;/span&gt; file-system &lt;a class="footnote-reference" href="#fat-variants" id="footnote-reference-6"&gt;[6]&lt;/a&gt;, so all the following assets &lt;em&gt;must&lt;/em&gt; be
placed on &lt;span class="caps"&gt;FAT&lt;/span&gt; partition(s)&amp;nbsp;somewhere:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Any bootloader assets that don&amp;#8217;t live in &lt;span class="caps"&gt;EEPROM&lt;/span&gt; for the generation of Pis you
need to&amp;nbsp;support&lt;/li&gt;
&lt;li&gt;The base device-tree(s) of all models you need to&amp;nbsp;support&lt;/li&gt;
&lt;li&gt;All the device-tree overlays wanted&amp;nbsp;(optional)&lt;/li&gt;
&lt;li&gt;The Linux&amp;nbsp;kernel&lt;/li&gt;
&lt;li&gt;The &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Initial_ramdisk"&gt;initramfs&lt;/a&gt;&amp;nbsp;(optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="die-with-your-boots-on"&gt;
&lt;h2&gt;Die with your boots&amp;nbsp;on&lt;/h2&gt;
&lt;p&gt;What does Ubuntu&amp;#8217;s current boot set up look like, particularly with regard to
safety?&amp;nbsp;Erm…&lt;/p&gt;
&lt;p&gt;On the plus side we always keep two sets of boot assets around on the boot
partition. But I&amp;#8217;m afraid that&amp;#8217;s it for the good&amp;nbsp;news:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;There is no fallback facility. If your boot configuration is corrupted, if we
release a bad kernel update, etc. your boot will fail and it&amp;#8217;s up to you to
pick the&amp;nbsp;pieces&lt;/li&gt;
&lt;li&gt;The boot assets are spread across &lt;em&gt;many&lt;/em&gt; files (the aforementioned bootloader
files, the kernel, the initramfs, the base device trees, and a few hundred
overlays). But the backup files aren&amp;#8217;t in easy-to-swap directories: they&amp;#8217;re
simply next to their original files with a &amp;#8220;.bak&amp;#8221;&amp;nbsp;suffix.&lt;/li&gt;
&lt;li&gt;If you know enough shell scripting you &lt;em&gt;might&lt;/em&gt; figure out that &lt;tt class="docutils literal"&gt;for f in
$(find /boot/firmware &lt;span class="pre"&gt;-type&lt;/span&gt; f &lt;span class="pre"&gt;-name&lt;/span&gt; &lt;span class="pre"&gt;&amp;quot;*.bak&amp;quot;);&lt;/span&gt; do cp &amp;quot;$f&amp;quot; &lt;span class="pre"&gt;&amp;quot;${f%.bak}&amp;quot;;&lt;/span&gt; done&lt;/tt&gt;
is what you want &lt;a class="footnote-reference" href="#probably" id="footnote-reference-7"&gt;[7]&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Even if you figure this out, the old boot files &lt;em&gt;probably&lt;/em&gt; aren&amp;#8217;t there. If
flash-kernel has run more than once since your last boot (this is extremely
likely given it runs in response to initramfs rebuilds, kernel updates,
flash-kernel upgrades, and so on) the &amp;#8220;.bak&amp;#8221; files will simply be copies of
the &lt;em&gt;new&lt;/em&gt; boot&amp;nbsp;assets!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is Bad with a capital B. No automatic fallback, the manual fallback is
extremely painful, and is not even likely to&amp;nbsp;work.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="puttin-the-boot-in"&gt;
&lt;h2&gt;Puttin&amp;#8217; the boot&amp;nbsp;in&lt;/h2&gt;
&lt;p&gt;A little-known &lt;a class="footnote-reference" href="#little-known" id="footnote-reference-8"&gt;[8]&lt;/a&gt; facility of the Pi&amp;#8217;s bootloader is the
&amp;#8220;tryboot&amp;#8221; facility. This is a rather neat system designed to provide a robust
means to implement A/B booting. Classically, A/B booting involves having two
separate copies of all your critical boot assets, and having some mechanism to
switch between&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s say you booted from set &amp;#8220;A&amp;#8221;. A new kernel is released, and gets installed
into &amp;#8220;B&amp;#8221;, and the bootloader is set to boot from &amp;#8220;B&amp;#8221; instead. The bootloader
then needs some means of noticing if the &amp;#8220;B&amp;#8221; boot fails, falling back to &amp;#8220;A&amp;#8221;.
In some cases this is done by having the bootloader record state itself in some
persistent location, but the Pi&amp;#8217;s mechanism for this is quite ingenious: it
uses &lt;em&gt;ephemeral&lt;/em&gt; state &lt;a class="footnote-reference" href="#pm-rsts" id="footnote-reference-9"&gt;[9]&lt;/a&gt; to track that it should be trying &amp;#8220;B&amp;#8221; so
that any failure results in it falling back to&amp;nbsp;&amp;#8220;A&amp;#8221;.&lt;/p&gt;
&lt;p&gt;How does this work in practice? A typical boot-flow reads the following&amp;nbsp;files:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Stage 1 reads &lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt; from &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt; (if it&amp;nbsp;exists)&lt;/li&gt;
&lt;li&gt;Reads stage 2 (&lt;tt class="docutils literal"&gt;bootcode.bin&lt;/tt&gt;) from the aforementioned &lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt;
(the first by default, but from &lt;span class="caps"&gt;EEPROM&lt;/span&gt; on the Pi 4 and&amp;nbsp;5)&lt;/li&gt;
&lt;li&gt;Reads stage 3 (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;start*.elf&lt;/span&gt;&lt;/tt&gt;) from the aforementioned &lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt;
(from &lt;span class="caps"&gt;EEPROM&lt;/span&gt; on the Pi&amp;nbsp;5)&lt;/li&gt;
&lt;li&gt;Reads &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; to determine the rest of the configuration, which
specifies the locations of all remaining&amp;nbsp;assets&lt;/li&gt;
&lt;li&gt;Reads the kernel, initramfs, device-trees, overlays, etc. from the
&lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, if you reboot the Pi in &amp;#8220;tryboot&amp;#8221; mode this changes&amp;nbsp;to:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Stage 1 reads &lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt; from &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt; (if it exists), but
optionally reads a &lt;tt class="docutils literal"&gt;[tryboot]&lt;/tt&gt; section that may override
&lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt; (potentially directing it to a different&amp;nbsp;partition)&lt;/li&gt;
&lt;li&gt;Reads stage 2 (&lt;tt class="docutils literal"&gt;bootcode.bin&lt;/tt&gt;) from the (potentially overridden)
&lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt; (from &lt;span class="caps"&gt;EEPROM&lt;/span&gt; on the Pi 4 and&amp;nbsp;5)&lt;/li&gt;
&lt;li&gt;Reads stage 3 (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;start*.elf&lt;/span&gt;&lt;/tt&gt;) from the (potentially overridden)
&lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt; (from &lt;span class="caps"&gt;EEPROM&lt;/span&gt; on the Pi&amp;nbsp;5)&lt;/li&gt;
&lt;li&gt;Reads &lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt; (instead of &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;) which specifies the
locations of all remaining&amp;nbsp;assets&lt;/li&gt;
&lt;li&gt;Reads the kernel, initramfs, device-trees, overlays, etc. from the
(potentially overridden) &lt;tt class="docutils literal"&gt;boot_partition&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &amp;#8220;tryboot&amp;#8221; mode is initiated using the following command to reboot the&amp;nbsp;Pi:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;reboot&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'0 tryboot'&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This leads to a couple of typical designs for a tryboot-enabled boot
implementation on the&amp;nbsp;Pi…&lt;/p&gt;
&lt;div class="section" id="full-abs"&gt;
&lt;h3&gt;Full&amp;nbsp;ABs&lt;/h3&gt;
&lt;p&gt;A &amp;#8220;full&amp;#8221; A/B boot implementation consists of the following partition&amp;nbsp;layout:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Partition 1 (&lt;span class="caps"&gt;FAT&lt;/span&gt;)&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first"&gt;Just contains &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt; with the following content &lt;a class="footnote-reference" href="#no-comments" id="footnote-reference-10"&gt;[10]&lt;/a&gt;:&lt;/p&gt;
&lt;div class="last"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="na"&gt;boot_partition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="k"&gt;[tryboot]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="na"&gt;tryboot_a_b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="na"&gt;boot_partition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/dd&gt;
&lt;dt&gt;Partition 2 (&lt;span class="caps"&gt;FAT&lt;/span&gt;)&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first"&gt;Contains the &amp;#8220;A&amp;#8221; set of boot assets. This includes the stage 2 and 3
binaries (if support of older Pi models is required), the kernel,
initramfs, device-trees, overlays, etc. No directories required;
everything&amp;#8217;s in the&amp;nbsp;root.&lt;/p&gt;
&lt;p class="last"&gt;Boot configuration is stored in &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;. The kernel command line in
&lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; points to partition 4 as the&amp;nbsp;rootfs.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;Partition 3 (&lt;span class="caps"&gt;FAT&lt;/span&gt;)&lt;/dt&gt;
&lt;dd&gt;Contains the &amp;#8220;B&amp;#8221; set of boot assets. Identical layout to partition 2, but
&lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; points to partition 5 as the&amp;nbsp;rootfs.&lt;/dd&gt;
&lt;dt&gt;Partition&amp;nbsp;4&lt;/dt&gt;
&lt;dd&gt;The &amp;#8220;A&amp;#8221;&amp;nbsp;rootfs&lt;/dd&gt;
&lt;dt&gt;Partition&amp;nbsp;5&lt;/dt&gt;
&lt;dd&gt;The &amp;#8220;B&amp;#8221;&amp;nbsp;rootfs&lt;/dd&gt;
&lt;/dl&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Note the (highlighted) &lt;tt class="docutils literal"&gt;tryboot_a_b=1&lt;/tt&gt; line in &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt;. This
causes the third stage to read &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; instead of &lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt;
because the assumption is that, in this layout, the boot configuration will
be precisely duplicated (both boot partitions have &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To switch between the A and B sets, &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt; on the first partition is
updated (preferably atomically &lt;a class="footnote-reference" href="#atomic-fs" id="footnote-reference-11"&gt;[11]&lt;/a&gt;) with swapped values of the
&lt;tt class="docutils literal"&gt;boot_partition=&lt;/tt&gt; lines.&lt;/p&gt;
&lt;p&gt;Typically the first partition is some smaller variant of &lt;span class="caps"&gt;FAT&lt;/span&gt; (&lt;span class="caps"&gt;FAT&lt;/span&gt;-12/16) while
partitions two and three are &lt;span class="caps"&gt;FAT&lt;/span&gt;-32. Partitions 4 and 5 are whatever you use as
a rootfs (commonly ext4, but can be anything your kernel and initramfs
combination can&amp;nbsp;support).&lt;/p&gt;
&lt;p&gt;The benefits of this layout are pretty&amp;nbsp;obvious:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It switches &lt;em&gt;everything&lt;/em&gt; including &lt;em&gt;all&lt;/em&gt; the bootloader assets (at least on
the Pi models without any &lt;span class="caps"&gt;EEPROM&lt;/span&gt;).&lt;/li&gt;
&lt;li&gt;It&amp;#8217;s also capable of switching the entire rootfs, although this is optional
(the &amp;#8220;B&amp;#8221; &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; could equally point at partition 4 to have a single&amp;nbsp;rootfs).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The drawbacks&amp;nbsp;are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;You pretty much have to design this &amp;#8220;up front&amp;#8221; into your image; it&amp;#8217;s great
for fresh installations, but almost impossible to &lt;em&gt;safely&lt;/em&gt; migrate existing
installations with a more basic layout to&amp;nbsp;it.&lt;/li&gt;
&lt;li&gt;This design is also more complex for users who wish to edit the boot
configuration as they now need to figure out which partition is mounted and
edit the boot files on the &lt;em&gt;other&lt;/em&gt; partition &lt;a class="footnote-reference" href="#experiments" id="footnote-reference-12"&gt;[12]&lt;/a&gt;. That said, I
suspect most uses of this layout try and ensure that users have no ability to
mess with the boot configuration at&amp;nbsp;all.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-beer-belly-version"&gt;
&lt;h3&gt;The beer belly&amp;nbsp;version&lt;/h3&gt;
&lt;p&gt;A much simpler configuration uses the traditional partition layout but with
some minor tweaks to the files on the boot&amp;nbsp;partition:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Partition 1 (&lt;span class="caps"&gt;FAT&lt;/span&gt;-32)&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="first last"&gt;
&lt;li&gt;&lt;p class="first"&gt;No &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt; (as per&amp;nbsp;usual)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Bootloader assets (&lt;tt class="docutils literal"&gt;bootcode.bin&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;start*.elf&lt;/span&gt;&lt;/tt&gt;) are placed in the
root of this&amp;nbsp;partition.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The &lt;tt class="docutils literal"&gt;a/&lt;/tt&gt; directory contains the remainder of the boot assets:
device-trees, Linux kernel, initramfs, overlays, &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; (which
points at partition 2 as the&amp;nbsp;rootfs).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Another directory, &lt;tt class="docutils literal"&gt;b/&lt;/tt&gt; contains a second set of these boot&amp;nbsp;assets.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; contains something like the&amp;nbsp;following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="hll"&gt;&lt;span class="na"&gt;os_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;a/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="na"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt; contains something&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="hll"&gt;&lt;span class="na"&gt;os_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;b/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="na"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;dt&gt;Partition&amp;nbsp;2&lt;/dt&gt;
&lt;dd&gt;The&amp;nbsp;rootfs&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;To switch between A and B, either &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt; are
exchanged, or the &lt;tt class="docutils literal"&gt;a/&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;b/&lt;/tt&gt; directories are switched (preferably
atomically &lt;a class="footnote-reference" href="#atomic-exchg" id="footnote-reference-13"&gt;[13]&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The advantages to this layout are primarily&amp;nbsp;simplicity.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It&amp;#8217;s fairly easy to migrate an existing classical layout to this, and do so
in a &amp;#8220;safe&amp;#8221; manner that guarantees a &amp;#8220;known good&amp;#8221; boot configuration is
present at every&amp;nbsp;step.&lt;/li&gt;
&lt;li&gt;It&amp;#8217;s also easier for users who know all their boot configuration will be
present on a single partition (they don&amp;#8217;t need to go potentially mounting&amp;nbsp;things).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The disadvantages&amp;nbsp;are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The bootloader assets (&lt;tt class="docutils literal"&gt;bootcode.bin&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;start*.elf&lt;/span&gt;&lt;/tt&gt;) cannot be switched
by this method as they cannot be read from sub-directories. However, bear in
mind that these are only used by older models of Pi and thus are unlikely to
change much in the coming years&amp;nbsp;anyway.&lt;/li&gt;
&lt;li&gt;Changing the boot configuration becomes a little more complex. Let&amp;#8217;s say you
want to try a new overlay. You add the &lt;tt class="docutils literal"&gt;dtoverlay=&lt;/tt&gt; line to &lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt;
and initiate &amp;#8220;tryboot&amp;#8221;. Your &lt;span class="caps"&gt;OS&lt;/span&gt; boots successfully, and some service switches
&lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;. However, now you need to remember to make
the same edit to the &lt;em&gt;new&lt;/em&gt; &lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt; (your former &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;) or
the next switch will lose the&amp;nbsp;change.&lt;/li&gt;
&lt;li&gt;If you want to edit the kernel command line, you still have to query
&lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; to figure out which &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; to fiddle with
(&lt;tt class="docutils literal"&gt;a/cmdline.txt&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;b/cmdline.txt&lt;/tt&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="boot-camp"&gt;
&lt;h2&gt;Boot&amp;nbsp;camp&lt;/h2&gt;
&lt;p&gt;As you can probably tell from the above, I don&amp;#8217;t think either of the two
designs presented is &lt;em&gt;quite&lt;/em&gt; what we want for&amp;nbsp;questing.&lt;/p&gt;
&lt;p&gt;The &amp;#8220;full&amp;#8221; A/B design is far too complex to migrate to, and I don&amp;#8217;t think
there&amp;#8217;s any way of doing it safely. This is especially the case given that we
may have upgraders who have inherited a minimal &lt;span class="caps"&gt;256MB&lt;/span&gt; boot partition from
earlier releases of&amp;nbsp;Ubuntu.&lt;/p&gt;
&lt;p&gt;The &amp;#8220;lite&amp;#8221; version is a good basis, but the whole &amp;#8220;a&amp;#8221; and &amp;#8220;b&amp;#8221; thing strikes me
as a little obscure. I also don&amp;#8217;t particularly like the duplication of
&lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; into &lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt;. There are an absolute ton of scripts out
there that assume they can blindly append configuration to &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;
(which is why it&amp;#8217;s usually important to make sure your configuration ends with
an &lt;tt class="docutils literal"&gt;[all]&lt;/tt&gt; section). While this isn&amp;#8217;t &amp;#8220;good practice&amp;#8221;, the swapping of
&lt;tt class="docutils literal"&gt;tryboot.txt&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; would break people who rely on these&amp;nbsp;scripts.&lt;/p&gt;
&lt;p&gt;After experimenting with a few designs over the last few weeks, here&amp;#8217;s the
design I&amp;#8217;ve come up with, which should be landing in questing as I post&amp;nbsp;this:&lt;/p&gt;
&lt;p&gt;As in the &amp;#8220;lite&amp;#8221; A/B boot design, we still have our regular old &lt;span class="caps"&gt;FAT&lt;/span&gt; boot
partition, and one rootfs partition. The bootloader assets (&lt;tt class="docutils literal"&gt;bootcode.bin&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;start*.elf&lt;/span&gt;&lt;/tt&gt;, and so on) still live in the root of the boot partition
(because they have to). All other assets move into three&amp;nbsp;directories:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;current&lt;/dt&gt;
&lt;dd&gt;This directory &lt;em&gt;always&lt;/em&gt; exists, and &lt;em&gt;always&lt;/em&gt; contains the &lt;em&gt;current&lt;/em&gt; boot
assets of the booted system &lt;a class="footnote-reference" href="#transitional" id="footnote-reference-14"&gt;[14]&lt;/a&gt;. Thus, by definition, it will
always contain &amp;#8220;known good&amp;#8221; boot&amp;nbsp;assets.&lt;/dd&gt;
&lt;dt&gt;old&lt;/dt&gt;
&lt;dd&gt;If this directory exists, it contains the formerly &amp;#8220;known good&amp;#8221; boot
assets. We keep this around when possible just in case users need it;
consider a boot configuration change that isn&amp;#8217;t fatal (the boot still
succeeds), but which results in some undesirable side effect later at
runtime. The new service should make it trivial to switch &amp;#8220;old&amp;#8221; to
&amp;#8220;current&amp;#8221; when&amp;nbsp;desired.&lt;/dd&gt;
&lt;dt&gt;new&lt;/dt&gt;
&lt;dd&gt;If this directory exists, it either contains new untested boot assets, or
it contains boot assets that were tested, but&amp;nbsp;failed.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Why keep failed boot assets around? It may be useful to see the configuration
that failed for debugging purposes, but more importantly we can only assume
that we have space for two sets of boot assets on the boot partition.
Therefore, before &amp;#8220;new&amp;#8221; is created, &amp;#8220;old&amp;#8221; is always deleted first. This is safe
because &amp;#8220;current&amp;#8221; is always &amp;#8220;known&amp;nbsp;good&amp;#8221;.&lt;/p&gt;
&lt;p&gt;So, we have the following &amp;#8220;states&amp;#8221; in our new boot&amp;nbsp;layout:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;stable&lt;/dt&gt;
&lt;dd&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;current&amp;#8221; exists; &amp;#8220;old&amp;#8221; &lt;em&gt;may&lt;/em&gt; exist (if it does, contains &amp;#8220;known good&amp;#8221;
assets); &amp;#8220;new&amp;#8221; &lt;em&gt;does not&lt;/em&gt;&amp;nbsp;exist&lt;/dd&gt;
&lt;dt&gt;untested&lt;/dt&gt;
&lt;dd&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;current&amp;#8221; exists; &amp;#8220;old&amp;#8221; &lt;em&gt;does not&lt;/em&gt; exist; &amp;#8220;new&amp;#8221; exists and contains
untested boot&amp;nbsp;assets&lt;/dd&gt;
&lt;dt&gt;trying&lt;/dt&gt;
&lt;dd&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;current&amp;#8221; exists; &amp;#8220;old&amp;#8221; &lt;em&gt;does not&lt;/em&gt; exist; &amp;#8220;new&amp;#8221; exists and contains
boot assets we&amp;#8217;re about to try (this state is entered immediately before
rebooting into the &amp;#8220;tryboot&amp;#8221;&amp;nbsp;mode)&lt;/dd&gt;
&lt;dt&gt;failed&lt;/dt&gt;
&lt;dd&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;current&amp;#8221; exists; &amp;#8220;old&amp;#8221; &lt;em&gt;does not&lt;/em&gt; exist; &amp;#8220;new&amp;#8221; exists and contains boot
assets marked as having&amp;nbsp;failed&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;The state transition diagram is as&amp;nbsp;follows:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/ab_states.svg" type="image/svg+xml"&gt;A simple static transition diagram showing &amp;#8220;stable&amp;#8221; goes to
&amp;#8220;untested&amp;#8221;; &amp;#8220;untested&amp;#8221; can go to &amp;#8220;untested&amp;#8221; or &amp;#8220;trying&amp;#8221;; &amp;#8220;trying&amp;#8221;
goes back to &amp;#8220;stable&amp;#8221; or to &amp;#8220;bad&amp;#8221;; and &amp;#8220;bad&amp;#8221; can only go to
&amp;#8220;untested&amp;#8221;.&lt;/object&gt;
&lt;p&gt;The &amp;#8220;loop&amp;#8221; on &amp;#8220;untested&amp;#8221; exists because (as mentioned earlier) it&amp;#8217;s perfectly
valid to have a long running system where flash-kernel has been called multiple
times (a kernel update, an initramfs rebuild, etc.), overwriting the new boot
assets each time. However, in this scenario the current boot assets are never
touched by&amp;nbsp;this.&lt;/p&gt;
&lt;p&gt;The boot configuration is stored in &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; and looks something like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="hll"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="hll"&gt;&lt;span class="na"&gt;os_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;current/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="hll"&gt;&lt;span class="k"&gt;[tryboot]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="na"&gt;os_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;new/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="c1"&gt;# The rest of config.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt; also exists, and just&amp;nbsp;contains:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="na"&gt;tryboot_a_b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This ensures the bootloader &lt;em&gt;always&lt;/em&gt; reads &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;, and we simply use a
&lt;tt class="docutils literal"&gt;[tryboot]&lt;/tt&gt; filter within that file to redirect our boot to the &amp;#8220;new&amp;#8221;
directory. The advantages of this layout&amp;nbsp;are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;We get all the advantages of the &amp;#8220;lite&amp;#8221; A/B boot setup: a reliable fallback,
and a configuration that we can migrate to &lt;em&gt;safely&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The boot configuration lives in a single file (&lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;) that doesn&amp;#8217;t
get arbitrarily over-written; existing scripts that append configuration to
&lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; continue to work without configuration lines silently&amp;nbsp;disappearing.&lt;/li&gt;
&lt;li&gt;There&amp;#8217;s no confusion over which boot assets are current, which are old, and
which are new: the directory names tell you&amp;nbsp;everything.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="booty-yarrr"&gt;
&lt;h2&gt;Booty!&amp;nbsp;Yarrr!&lt;/h2&gt;
&lt;p&gt;This all sounds a bit too good to be true! What are the&amp;nbsp;drawbacks?&lt;/p&gt;
&lt;p&gt;The most obvious one is the fact that the &amp;#8220;tryboot&amp;#8221; mode can never be entered
from a cold boot. It &lt;em&gt;requires&lt;/em&gt; a reboot. This means some service needs to
notice (during a normal boot) that new untested boot assets are present,
interrupt the boot and restart in &amp;#8220;tryboot&amp;#8221; mode. This will mean that, each
time &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; is run for whatever reason (new kernel, initramfs
rebuild, etc.) the next boot will be a double boot. This will probably be a bit
jarring to people at first (&amp;#8220;why did I see the rainbow screen &lt;em&gt;twice&lt;/em&gt;?!&amp;#8221;), and
also means that boot will take roughly twice as long&amp;nbsp;(obviously).&lt;/p&gt;
&lt;p&gt;However, I don&amp;#8217;t see a way around this and, to be frank, it&amp;#8217;s a small price to
pay for the reliability that this mechanism should&amp;nbsp;bring.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tough-as-old-boots"&gt;
&lt;h2&gt;Tough as old&amp;nbsp;boots&lt;/h2&gt;
&lt;p&gt;I don&amp;#8217;t mean all this to sound like a fait accompli &lt;a class="footnote-reference" href="#it-is" id="footnote-reference-15"&gt;[15]&lt;/a&gt;. Some people do
weird things with their Pi. Some people may be relying on all their boot assets
being in the root of their boot partition. Some may have &lt;em&gt;weird hardware&lt;/em&gt; that
breaks horribly if reboots occur too close together (or which doesn&amp;#8217;t reset
quickly enough for the second&amp;nbsp;boot).&lt;/p&gt;
&lt;p&gt;In short, there &lt;em&gt;must&lt;/em&gt; be a fallback mechanism. To that end, I&amp;#8217;m introducing
these changes as a new &amp;#8220;method&amp;#8221; in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt;, but keeping the old one in
place for those that &lt;em&gt;really&lt;/em&gt; need&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;All boards that use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; to write their boot assets (including the
Raspberry Pi under Ubuntu) have entries in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; database
(&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/share/flash-kernel/db/all.db&lt;/span&gt;&lt;/tt&gt;) which look something like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;Machine: Raspberry Pi 3 Model B+
&lt;/span&gt;&lt;span id="line-2"&gt;Machine: Raspberry Pi 3 Model B Plus
&lt;/span&gt;&lt;span id="line-3"&gt;Machine: Raspberry Pi 3 Model B Plus Rev 1.3
&lt;/span&gt;&lt;span id="line-4"&gt;Machine: Raspberry Pi 3 Model B Plus Rev *
&lt;/span&gt;&lt;span id="line-5"&gt;Kernel-Flavors: raspi
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;Method: pi-try
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;DTB-Id: bcm2710-rpi-3-b-plus.dtb
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The important setting for our purposes is the &lt;tt class="docutils literal"&gt;Method:&lt;/tt&gt; line (highlighted).
Prior to questing this reads &lt;tt class="docutils literal"&gt;Method: pi&lt;/tt&gt; on all the (relevant) Raspberry Pi
entries. From questing onwards this will read &lt;tt class="docutils literal"&gt;Method: &lt;span class="pre"&gt;pi-try&lt;/span&gt;&lt;/tt&gt;. When
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; encounters the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pi-try&lt;/span&gt;&lt;/tt&gt; method, and finds the old boot
configuration on the boot partition, it will attempt to migrate it to the new
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pi-try&lt;/span&gt;&lt;/tt&gt; layout, and re-write the &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; configuration&amp;nbsp;accordingly.&lt;/p&gt;
&lt;p&gt;However, the older &lt;tt class="docutils literal"&gt;pi&lt;/tt&gt; method (no A/B booting, write everything to the root
of the boot partition) is still present in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt;, and if you
absolutely need to, you can switch back to&amp;nbsp;it.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p class="last"&gt;Using the old &amp;#8220;pi&amp;#8221; mechanism leaves you in the situation described earlier:
your fallback &amp;#8220;.bak&amp;#8221; files may not really be a fallback at all, and even if
they are, it&amp;#8217;s a major pain to revert to them. Still, if you&amp;#8217;re
&lt;em&gt;absolutely&lt;/em&gt;&amp;nbsp;determined…&lt;/p&gt;
&lt;/div&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Firstly, override the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; database entry by copying the
relevant entry to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/flash-kernel/db&lt;/span&gt;&lt;/tt&gt; and adjusting the &lt;tt class="docutils literal"&gt;Method:&lt;/tt&gt;
line.&lt;/li&gt;
&lt;li&gt;Then, remove the &lt;tt class="docutils literal"&gt;old/&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;new/&lt;/tt&gt; folders from your boot&amp;nbsp;partition.&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; to re-copy the boot assets to the root of your boot&amp;nbsp;partition.&lt;/li&gt;
&lt;li&gt;Remove the &lt;tt class="docutils literal"&gt;os_prefix&lt;/tt&gt; lines from your boot&amp;nbsp;configuration.&lt;/li&gt;
&lt;li&gt;Finally remove the &lt;tt class="docutils literal"&gt;current/&lt;/tt&gt; directory and &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Done in this specific order, this procedure &lt;em&gt;should&lt;/em&gt; be reasonably safe and
leave you with a bootable system at all&amp;nbsp;steps.&lt;/p&gt;
&lt;p&gt;By way of an example, in the case of the Pi 3 entry above, the following
&lt;em&gt;should&lt;/em&gt; be sufficient to revert to the old boot method (pay close attention to
the highlighted&amp;nbsp;lines):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;-i
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="go"&gt;Password:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/flash-kernel/db
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="go"&gt;Machine: Raspberry Pi 3 Model B+&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="go"&gt;Machine: Raspberry Pi 3 Model B Plus&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="go"&gt;Machine: Raspberry Pi 3 Model B Plus Rev 1.3&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="go"&gt;Machine: Raspberry Pi 3 Model B Plus Rev *&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="go"&gt;Kernel-Flavors: raspi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="hll"&gt;&lt;span class="go"&gt;Method: pi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="go"&gt;DTB-Id: bcm2710-rpi-3-b-plus.dtb&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="go"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/old
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/new
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;flash-kernel
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sync
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/^os_prefix/d&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/config.txt
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/current
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/autoboot.txt
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Anyway, that&amp;#8217;s enough &amp;#8220;boot&amp;#8221; puns from me, for now. If you do &lt;em&gt;anything&lt;/em&gt; even
slightly weird with your boot configuration, I would &lt;em&gt;strongly&lt;/em&gt; encourage you
to try out the questing dailies on a spare &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://cdimage.ubuntu.com/ubuntu/daily-preinstalled/current/"&gt;Questing desktop&amp;nbsp;dailies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://cdimage.ubuntu.com/ubuntu-server/daily-preinstalled/current/"&gt;Questing server&amp;nbsp;dailies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To test the new implementation from a questing daily, add
&lt;a class="reference external" href="https://launchpad.net/~waveform/+archive/ubuntu/flash-kernel"&gt;ppa:waveform/flash-kernel&lt;/a&gt; and install the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel-piboot&lt;/span&gt;&lt;/tt&gt; package:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;add-apt-repository&lt;span class="w"&gt; &lt;/span&gt;ppa:waveform/flash-kernel
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;flash-kernel-piboot
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will also upgrade &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; and migrate your boot partition to the
new layout. In &lt;em&gt;theory&lt;/em&gt; the migration is entirely safe, i.e. it can fail at any
point and you &lt;em&gt;should&lt;/em&gt; be left with a bootable system (I&amp;#8217;ve tested this a
couple of times by yanking the power-cord in the middle of it!). However, if
you can find a way to break it (from a configuration we&amp;#8217;d actually support),
then please let me&amp;nbsp;know!&lt;/p&gt;
&lt;p&gt;If you find any issues, please &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/flash-kernel/+filebug"&gt;file a bug&lt;/a&gt; against the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt;
package in Launchpad, and tag it &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-image&lt;/span&gt;&lt;/tt&gt; to bring it to my&amp;nbsp;attention.&lt;/p&gt;
&lt;p&gt;If you &lt;em&gt;do&lt;/em&gt; find a genuine need to fall back to the old boot configuration,
again &lt;em&gt;please&lt;/em&gt; let me know! I&amp;#8217;d be really curious to find out what these
circumstances are; I&amp;#8217;m sure they exist, but they&amp;#8217;ll be things I haven&amp;#8217;t thought
of (yet), and if there&amp;#8217;s a way I can tweak the new design to accommodate them,
I&amp;#8217;d prefer to do that rather than have people forcing themselves back into the
old (fundamentally unsafe)&amp;nbsp;configuration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="boots-of-spanish-leather"&gt;
&lt;h2&gt;Boots of Spanish&amp;nbsp;Leather&lt;/h2&gt;
&lt;p&gt;The alternative to falling back to the old mechanism is to keep the A/B
facility, but manage the tryboot mode somewhat&amp;nbsp;manually…&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;piboot-try&lt;/span&gt;&lt;/tt&gt; command is the new script that manages all the stuff
mentioned above (which will be provided by the new &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel-piboot&lt;/span&gt;&lt;/tt&gt;
package, hopefully in Ubuntu questing by next week. When called with the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--test&lt;/span&gt;&lt;/tt&gt; flag this will exit with status code 0 (&amp;#8220;true&amp;#8221; in shell parlance) in
the event there are new, untested boot assets. Otherwise, it will exit with
status code 1 (&amp;#8220;false&amp;#8221; or &amp;#8220;error&amp;#8221; in&amp;nbsp;shells).&lt;/p&gt;
&lt;p&gt;When called with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--reboot&lt;/span&gt;&lt;/tt&gt;, if new untested boot assets are present, it will
immediately re-write their status to &amp;#8220;trying&amp;#8221; and reboot into the &amp;#8220;tryboot&amp;#8221;&amp;nbsp;mode.&lt;/p&gt;
&lt;p&gt;Hence, if you know flash-kernel has run, and new boot assets are present, and
you&amp;#8217;re happy to reboot immediately you can simply run &lt;tt class="docutils literal"&gt;sudo &lt;span class="pre"&gt;piboot-try&lt;/span&gt;
&lt;span class="pre"&gt;--reboot&lt;/span&gt;&lt;/tt&gt; to reboot and immediately try the new boot assets (no double boot
necessary). If you&amp;#8217;re thinking of scripting this and want to query the state of
the boot assets, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;piboot-try&lt;/span&gt; &lt;span class="pre"&gt;--test&lt;/span&gt;&lt;/tt&gt; (no &lt;tt class="docutils literal"&gt;sudo&lt;/tt&gt; necessary) will tell you
whether &lt;tt class="docutils literal"&gt;sudo &lt;span class="pre"&gt;piboot-try&lt;/span&gt; &lt;span class="pre"&gt;--reboot&lt;/span&gt;&lt;/tt&gt; &lt;em&gt;would&lt;/em&gt; reboot (if the exit code is&amp;nbsp;0).&lt;/p&gt;
&lt;p&gt;If you want to learn more about the command, I&amp;#8217;ll be including a full man-page
for it, &lt;tt class="docutils literal"&gt;man &lt;span class="pre"&gt;piboot-try&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="kernel-only" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;On the Pi 5, none of the bootloader resides on the &lt;span class="caps"&gt;FAT&lt;/span&gt;
partition anymore. In certain configurations, you can even get away with no
configuration file, so just shoving a Linux kernel on the &lt;span class="caps"&gt;FAT&lt;/span&gt; partition is
enough!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="cut-down" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Intended to maximize the &lt;span class="caps"&gt;RAM&lt;/span&gt; available at runtime (the default
bootloader typically reserved &lt;span class="caps"&gt;64MB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt; for facilities it provided at
runtime; the cut down variant only needed &lt;span class="caps"&gt;16MB&lt;/span&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="gpu" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;One particularly odd aspect of the Pi&amp;#8217;s boot process is that it runs
entirely on the &lt;span class="caps"&gt;GPU&lt;/span&gt; (well … &lt;span class="caps"&gt;VPU&lt;/span&gt;?), rather than the &lt;span class="caps"&gt;ARM&lt;/span&gt; &lt;span class="caps"&gt;CPU&lt;/span&gt;. The legacy
camera firmware basically ran its own &lt;span class="caps"&gt;RTOS&lt;/span&gt; over on the &lt;span class="caps"&gt;GPU&lt;/span&gt;, hence
incorporating it into the bootloader made perfect sense.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="maybe-dt" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Actually, I&amp;#8217;m not entirely clear if stage 3 loads the &lt;em&gt;base&lt;/em&gt;
device tree. That &lt;em&gt;might&lt;/em&gt; be stage 2, but stage 3 handles some of the
customization of the device-tree, loading overlays, and so forth.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="afaik" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I&amp;#8217;m not entirely clear on what &lt;tt class="docutils literal"&gt;fixup.dat&lt;/tt&gt; really does either. I
know each fixup file corresponds to each &lt;tt class="docutils literal"&gt;start.elf&lt;/tt&gt; file and that it has
&lt;em&gt;something&lt;/em&gt; to do with the &lt;span class="caps"&gt;CPU&lt;/span&gt;/&lt;span class="caps"&gt;GPU&lt;/span&gt; memory split. The &lt;tt class="docutils literal"&gt;start.elf&lt;/tt&gt; binary
will operate without &lt;tt class="docutils literal"&gt;fixup.dat&lt;/tt&gt; being present, but the wrong amount of
&lt;span class="caps"&gt;RAM&lt;/span&gt; is reported by the mailbox interface in this case. I&amp;#8217;ve also heard fixup
has to do with &amp;#8220;relocation&amp;#8221;, and I do know that the &lt;span class="caps"&gt;CPU&lt;/span&gt;/&lt;span class="caps"&gt;GPU&lt;/span&gt; memory split has
the &lt;span class="caps"&gt;CPU&lt;/span&gt; (&lt;span class="caps"&gt;ARM&lt;/span&gt; portion) at the &amp;#8220;low&amp;#8221; end and the &lt;span class="caps"&gt;GPU&lt;/span&gt; firmware at the &amp;#8220;high&amp;#8221;
end so my vaguely educated guess is that &lt;tt class="docutils literal"&gt;start.elf&lt;/tt&gt; gets loaded at a
too-low, but definitely safe location, then &lt;tt class="docutils literal"&gt;fixup.dat&lt;/tt&gt; is used to
relocate it to the highest possible point it can safely sit in &lt;span class="caps"&gt;RAM&lt;/span&gt; to
maximize the amount available to the &lt;span class="caps"&gt;ARM&lt;/span&gt; cores. But that&amp;#8217;s just my guess.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="fat-variants" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The stages all understand &lt;span class="caps"&gt;FAT&lt;/span&gt;-12, &lt;span class="caps"&gt;FAT&lt;/span&gt;-16, &lt;span class="caps"&gt;FAT&lt;/span&gt;-32, and the
&lt;span class="caps"&gt;VFAT&lt;/span&gt; long filename extensions.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="probably" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-7"&gt;[7]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Maybe… This is just off the top of my head; I haven&amp;#8217;t tested
it and give no warranty that this will (or won&amp;#8217;t!) unbrick your boot!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="little-known" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-8"&gt;[8]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Maybe, given some interactions I&amp;#8217;ve had online?&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="pm-rsts" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-9"&gt;[9]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The state is stored in the PM_RSTS register on the &lt;abbr title="Power Management IC"&gt;&lt;span class="caps"&gt;PMIC&lt;/span&gt;&lt;/abbr&gt;. &lt;span class="caps"&gt;PMIC&lt;/span&gt; registers (generally) survive reset (obviously
not power off though), but this particular one is also reset-on-read so it
is guaranteed subsequent boots will fall back if the tryboot one fails for
any reason.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="no-comments" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-10"&gt;[10]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The &lt;tt class="docutils literal"&gt;autoboot.txt&lt;/tt&gt; file has a size limit of 512 bytes
(presumably because stage 1 has to be incredibly basic, and can&amp;#8217;t read
&lt;span class="caps"&gt;FAT&lt;/span&gt; chains, so the file is limited to one sector). In other words don&amp;#8217;t
include extraneous comments or line breaks!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="atomic-fs" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-11"&gt;[11]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Bear in mind that &lt;span class="caps"&gt;FAT&lt;/span&gt; is not a journalling file-system. Updates
to any boot configuration should generally be done atomically by writing the
new content to a temporary file on the target &lt;span class="caps"&gt;FAT&lt;/span&gt; partition, then renaming
the temporary file over the original. The rename operation is atomic so
anything reading the file-system should either see the original content or
the new content, but no partially written file.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="experiments" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-12"&gt;[12]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Under the assumption that you shouldn&amp;#8217;t ever be fiddling with
your &amp;#8220;known good&amp;#8221; boot configuration. All experiments should be performed in
the &amp;#8220;alternate&amp;#8221; boot assets which are then tested with tryboot before being
made the new &amp;#8220;known good&amp;#8221; set.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="atomic-exchg" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-13"&gt;[13]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;With &lt;span class="caps"&gt;GNU&lt;/span&gt; coreutils 9.5 and above, simply: &lt;tt class="docutils literal"&gt;mv &lt;span class="pre"&gt;--exchange&lt;/span&gt;
config.txt tryboot.txt&lt;/tt&gt;!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="transitional" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-14"&gt;[14]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This is a slight lie. There are some brief transitional
states where &lt;em&gt;current&lt;/em&gt; exists, but contains former assets. These should
always be rapidly corrected, as we&amp;#8217;ll see.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="it-is" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-15"&gt;[15]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Though it kinda is… I do control the boot process on your Pi after
all! Mwuhahahaha!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="questing"></category><category term="pi"></category><category term="boot"></category></entry><entry><title>D0!</title><link href="https://waldorf.waveform.org.uk/2025/d0.html" rel="alternate"></link><published>2025-07-04T00:00:00+01:00</published><updated>2025-07-11T11:58:36+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2025-07-04:/2025/d0.html</id><summary type="html">&lt;p class="first last"&gt;A look at how new hardware and lengthy dependency chains combine to
make&amp;nbsp;trouble!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;Updated&lt;/strong&gt;: To include the picture I forgot, and fix a bunch of&amp;nbsp;typos&lt;/p&gt;
&lt;p&gt;Dear&amp;nbsp;Reader,&lt;/p&gt;
&lt;p&gt;Please be forewarned that this is a rambling mess of a post that will dart off
on tangents a-plenty. This was originally planned to be posted before noble&amp;#8217;s
.2 release, but too many work things go in the way and it&amp;#8217;s laid in the drafts
folder until I noticed it again while writing something&amp;nbsp;else!&lt;/p&gt;
&lt;p&gt;Anyway, this may at least be an entertaining distraction into the effort that
goes in to dealing with hardware changes in&amp;nbsp;Ubuntu.&lt;/p&gt;
&lt;p&gt;Buckle&amp;nbsp;up!&lt;/p&gt;
&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;First of all… what&amp;#8217;s a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Stepping_level"&gt;stepping&lt;/a&gt;? Put simply, it&amp;#8217;s a specific version of a
particular chip. A new stepping might be released to fix a bug, or improve
yields, or make other improvements to a design, but it isn&amp;#8217;t a fundamentally
new design. Think of it like a bump in a minor version&amp;nbsp;number.&lt;/p&gt;
&lt;p&gt;There have been new steppings of Raspberry Pi &lt;a class="reference external" href="https://en.wikipedia.org/wiki/System_on_a_chip"&gt;SoCs&lt;/a&gt; in the past, but
you may not have noticed. For example, the Pi 4 uses the Broadcom 2711 as its
SoC. At launch, this was the B0 stepping. However, when the Compute Module 4
launched later, it moved to the C0 stepping, which corrected an issue in the
boot loader&amp;#8217;s verification mechanism. I &lt;em&gt;think&lt;/em&gt; later versions of the Pi 4
moved to this stepping as well, but looking at my range of Pi 4 boards here,
they&amp;#8217;re all&amp;nbsp;B0s.&lt;/p&gt;
&lt;p&gt;What about the Pi 5? The &lt;span class="caps"&gt;4GB&lt;/span&gt; and &lt;span class="caps"&gt;8GB&lt;/span&gt; models initially launched, shipped with
the C1 stepping of the Broadcom 2712 SoC. However, Broadcom&amp;#8217;s initial design of
the 2712 included several features which were never used in the Pi 5,
increasing the cost and the power draw of the chip without any benefit. So much
so, that initially the planned &lt;span class="caps"&gt;2GB&lt;/span&gt; variant of the Pi 5 was uneconomic to&amp;nbsp;produce.&lt;/p&gt;
&lt;p&gt;The new 2712 D0 stepping stripped out this &amp;#8220;&lt;a class="reference external" href="https://www.raspberrypi.com/news/2gb-raspberry-pi-5-on-sale-now-at-50/"&gt;dark silicon&lt;/a&gt;&amp;#8220;, reportedly as
much as 30% of the die, making the &lt;span class="caps"&gt;2GB&lt;/span&gt; possible to produce. It was also used in
the subsequent launches of the Compute Module 5, and later the &lt;span class="caps"&gt;16GB&lt;/span&gt; Pi 5
variant launched in January this year. The assumption is that at some point the
D0 will become the standard silicon on the &lt;span class="caps"&gt;4GB&lt;/span&gt; and &lt;span class="caps"&gt;8GB&lt;/span&gt; models&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;So far, so good. But also, so what? Why does the &lt;span class="caps"&gt;OS&lt;/span&gt; care about the&amp;nbsp;stepping?&lt;/p&gt;
&lt;p&gt;The D0 stepping introduced another change: one of the data structures
associated with the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Shader"&gt;shaders&lt;/a&gt; grew some new fields in the &lt;em&gt;middle&lt;/em&gt;
of the structure, shuffling the locations of other fields. Stripping out some
details of the structures to make the diff a bit easier to&amp;nbsp;read:&lt;/p&gt;
&lt;pre class="code diff literal-block"&gt;
&lt;span class="gd"&gt;-&amp;lt;struct name=&amp;quot;GL Shader State Record&amp;quot; min_ver=&amp;quot;71&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+&amp;lt;struct name=&amp;quot;GL Shader State Record Draw Index&amp;quot; min_ver=&amp;quot;71&amp;quot;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Point size in shaded vertex data&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;
 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Enable clipping&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;

 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Vertex ID read by coordinate shader&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;
 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Instance ID read by coordinate shader&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;
 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Base Instance ID read by coordinate shader&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    &amp;lt;field name=&amp;quot;cs_basevertex&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    &amp;lt;field name=&amp;quot;cs_drawindex&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Vertex ID read by vertex shader&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;
 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Instance ID read by vertex shader&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;
 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Base Instance ID read by vertex shader&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    &amp;lt;field name=&amp;quot;vs_basevertex&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    &amp;lt;field name=&amp;quot;vs_drawindex&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;

 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Fragment shader does Z writes&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;&lt;span class="w"&gt;
 &lt;/span&gt;    &amp;lt;field name=&amp;quot;Turn off early-z test&amp;quot; size=&amp;quot;1&amp;quot; type=&amp;quot;bool&amp;quot;/&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This is a breaking change, requiring &lt;a class="reference external" href="https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/29189/diffs"&gt;changes&lt;/a&gt; in &lt;a class="reference external" href="https://gitlab.freedesktop.org/mesa/mesa"&gt;mesa&lt;/a&gt; (the
library that provides various graphical APIs including &lt;a class="reference external" href="https://en.wikipedia.org/wiki/OpenGL"&gt;OpenGL&lt;/a&gt; and
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Vulkan"&gt;Vulkan&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;So, how can you tell if you&amp;#8217;ve got a C1 or a D0 stepping of a 2712? You could
take the cooler off your Pi 5 and look at the lid of the SoC. The stepping is
just near the end of the long&amp;nbsp;code.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="Overhead view of a 2 Pi 5s. Zooms of their SoCs show that the top Pi 5 has the &amp;quot;C1&amp;quot; stepping, while the bottom one has the &amp;quot;D0&amp;quot; stepping." src="https://waldorf.waveform.org.uk/images/pi5-c1-d0.jpg" /&gt;
&lt;p class="caption"&gt;It&amp;#8217;s spot the difference time! The easy stepping difference has been
pointed out for you. Can you find the other two differences? No cheating by
looking at the &lt;a class="reference external" href="https://pip.raspberrypi.com/categories/1129-pcn/documents/RP-006041-PC/PCN-Raspberry-Pi-5B-Rev-4.pdf"&gt;&lt;span class="caps"&gt;PCN&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Alternatively, you could try and use Ubuntu 24.04.1 (noble&amp;#8217;s .1) Desktop image.
If you&amp;#8217;ve got a C1, it works! If you&amp;#8217;ve got a D0 … erm&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="raspios"&gt;
&lt;h2&gt;RaspiOS&lt;/h2&gt;
&lt;p&gt;How do we fix this? First let&amp;#8217;s look at RaspiOS. Obviously mesa needs updating,
with the requisite &lt;a class="reference external" href="https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/29189/diffs"&gt;changes&lt;/a&gt;. RaspiOS added these in version
23.2.1-1~bpo12+rpt3 with the following slightly unceremonious&amp;nbsp;changelog:&lt;/p&gt;
&lt;pre class="code text literal-block"&gt;
mesa (23.2.1-1~bpo12+rpt3) bookworm; urgency=medium

  * Update patches
&lt;/pre&gt;
&lt;p&gt;To be fair, this may have been a matter of trying to keep the existence of the
new stepping relatively quiet (though anyone constructing a debdiff would see
the 2712D0 mentioned quite prominently). The release date on that package is
January 2024, and the Pi 5 &lt;span class="caps"&gt;2GB&lt;/span&gt; model featuring the D0 didn&amp;#8217;t hit the market
until August&amp;nbsp;2024.&lt;/p&gt;
&lt;p&gt;Still, this is basically all that happens in RaspiOS &lt;a class="footnote-reference" href="#new-dts" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. New mesa, job&amp;nbsp;done.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ubuntu"&gt;
&lt;h2&gt;Ubuntu&lt;/h2&gt;
&lt;p&gt;Over in Ubuntu land, things are a bit more complex. As it turned out, the fix
was already in the development version (sync&amp;#8217;d from upstream), and even
Oracular (24.10 which is about to go &lt;abbr title="End of Life"&gt;&lt;span class="caps"&gt;EOL&lt;/span&gt;&lt;/abbr&gt; as I write this)
had a sufficiently up to date mesa to incorporate the&amp;nbsp;fix.&lt;/p&gt;
&lt;p&gt;However, the interim versions are not what most Ubuntu users are interested in.
The &lt;em&gt;vast&lt;/em&gt; majority of Ubuntu users (something like 99%) stick with the
&lt;abbr title="Long Term Service"&gt;&lt;span class="caps"&gt;LTS&lt;/span&gt;&lt;/abbr&gt; releases which at the time of writing is noble
(24.04). In order to fix things in an existing stable release, we needed an
&lt;abbr title="Stable Release Update"&gt;&lt;span class="caps"&gt;SRU&lt;/span&gt;&lt;/abbr&gt;…&lt;/p&gt;
&lt;div class="section" id="sru"&gt;
&lt;h3&gt;&lt;span class="caps"&gt;SRU&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;A quick tangent on Stable Release Updates, or SRUs as we refer to them (because
there&amp;#8217;s nothing more developers love than another &lt;abbr title="Three Letter Acronym"&gt;&lt;span class="caps"&gt;TLA&lt;/span&gt;&lt;/abbr&gt;). Actually, first another tangent on stability of Linux&amp;nbsp;distros.&lt;/p&gt;
&lt;p&gt;Linux distributions can be roughly divided into two categories: stable distros
and rolling distros. Stable distros grew out of the frustrations of server
operators who wanted a guarantee of stability above all else: once the system
was installed and working the &lt;em&gt;only&lt;/em&gt; updates they wished to see were those that
fixed bugs. They had precisely zero interest in new features and even minor bug
fixes were probably more risk than they were worth. The only time risk was
introduced into the equation was when the &lt;span class="caps"&gt;OS&lt;/span&gt; as a whole was updated (a
&amp;#8220;dist-upgrade&amp;#8221; in Debian terms), and because this was a known risk, it would be
planned for, tested, and implemented in a controlled&amp;nbsp;manner.&lt;/p&gt;
&lt;p&gt;Classic &amp;#8220;stable&amp;#8221; distros include Red Hat, Debian, and of course&amp;nbsp;Ubuntu.&lt;/p&gt;
&lt;p&gt;The rolling distros by contrast, have no concept of an &lt;span class="caps"&gt;OS&lt;/span&gt; &amp;#8220;release&amp;#8221;. They
simply package the latest versions of all things at all times. Much of the
time, this does work reasonably well, but it does mean that at certain times
there fairly major changes that happen in a more or less &amp;#8220;adhoc&amp;#8221; fashion.
Historically, consider the move to systemd, or transitions between Qt&amp;nbsp;versions.&lt;/p&gt;
&lt;p&gt;Classic &amp;#8220;rolling&amp;#8221; distros include Gentoo and&amp;nbsp;Arch.&lt;/p&gt;
&lt;p&gt;Some distros incorporate both models. Alpine and Debian are examples of these.
While Debian has a &amp;#8220;stable&amp;#8221; release (currently &amp;#8220;bookworm&amp;#8221;, soon to be
&amp;#8220;trixie&amp;#8221;), it also has the &amp;#8220;unstable&amp;#8221; branch called &amp;#8220;sid&amp;#8221; which is basically a
rolling distro in itself. Alpine likewise makes six monthly releases, but has a
rolling &amp;#8220;edge&amp;#8221;&amp;nbsp;branch.&lt;/p&gt;
&lt;p&gt;While the &amp;#8220;stable&amp;#8221; distro model is widely accepted (and very reasonable, in
this writer&amp;#8217;s opinion), it does cause some tension, particularly with the rise
of desktop Linux usage. Desktop users often &lt;em&gt;do&lt;/em&gt; want the fancy new features in
their applications, don&amp;#8217;t have the same level of risk-aversion as the typical
hard-boiled sysadmin &lt;a class="footnote-reference" href="#paranoid" id="footnote-reference-2"&gt;[2]&lt;/a&gt;, and thus aren&amp;#8217;t willing to wait 2 or 3 years
for the next release of their stable distro to get them. This is part of the
drive behind the rise of &amp;#8220;alternate&amp;#8221; packaging systems on various distros
(Flatpaks, Snaps, AppImages,&amp;nbsp;etc).&lt;/p&gt;
&lt;p&gt;On Ubuntu, updates to stable releases (outside of the realm of snaps) have to
follow the &lt;a class="reference external" href="https://documentation.ubuntu.com/sru/en/latest/"&gt;Stable Release Update&lt;/a&gt; process which incorporates numerous checks
and balances to try and avoid regressions. Test plans must be written,
reviewed, and agreed upon before anything is even uploaded. Updates must be
minimally invasive (no full version backports &amp;#8212; those have to follow a
separate process which doesn&amp;#8217;t automatically update existing installs). After
upload, verification is carried out, and only &lt;em&gt;then&lt;/em&gt; is the fix allowed into
the&amp;nbsp;release.&lt;/p&gt;
&lt;p&gt;But not right&amp;nbsp;away…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="phasing"&gt;
&lt;h3&gt;Phasing&lt;/h3&gt;
&lt;p&gt;Even after an update to a stable release has made it through review,
verification, and upload, such updates go through a &amp;#8220;phasing&amp;#8221; procedure. This
is where the update is slowly trickled out to larger and larger proportions of
the userbase. The idea is that, if an update does have a catastrophic bug in
it, the damage can be limited to the portion of the userbase that (randomly or
by choice) got the update early. In the event of a nasty bug appearing in a
stable update, phasing can be halted and the issue investigated before either
rolling back the update, or deciding it&amp;#8217;s a false-positive and resuming the&amp;nbsp;phasing.&lt;/p&gt;
&lt;p&gt;Incidentally, the &lt;a class="reference external" href="https://ubuntu-archive-team.ubuntu.com/pending-sru"&gt;stable release updates report&lt;/a&gt;, and the &lt;a class="reference external" href="https://ubuntu-archive-team.ubuntu.com/phased-updates.html"&gt;phasing report&lt;/a&gt;
are both public if you want to peak at what&amp;#8217;s in the pipeline of your stable
install! You can read more &lt;a class="reference external" href="https://documentation.ubuntu.com/server/explanation/software/about-apt-upgrade-and-phased-updates/"&gt;about phasing&lt;/a&gt; in the server&amp;nbsp;documentation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="noble"&gt;
&lt;h3&gt;Noble&lt;/h3&gt;
&lt;p&gt;Okay, back to the story. Mesa was up to date in oracular, but we needed an &lt;span class="caps"&gt;SRU&lt;/span&gt;
to noble to avoid new Pi 5 owners getting a full blown crash the second
anything graphical like a login screen dared show its face on the&amp;nbsp;monitor.&lt;/p&gt;
&lt;p&gt;Time for a noble &lt;span class="caps"&gt;SRU&lt;/span&gt;. The &lt;span class="caps"&gt;SRU&lt;/span&gt; was written (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/mesa/+bug/2082072"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #2082072&lt;/a&gt;), the update
sponsored, verification performed, phasing completed, and by the middle of
October everything was&amp;nbsp;fixed.&lt;/p&gt;
&lt;pre class="code text literal-block"&gt;
mesa (24.0.9-0ubuntu0.2) noble; urgency=medium

  * Add support for Pi 2712D0 stepping (LP: #2082072)
&lt;/pre&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;p&gt;I said, everything was &lt;em&gt;fixed&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;p&gt;Right?&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;p&gt;Oh,&amp;nbsp;****!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="snaps"&gt;
&lt;h3&gt;Snaps&lt;/h3&gt;
&lt;p&gt;One thing I&amp;#8217;d forgotten about in this whole process was that there&amp;#8217;s no longer
just &lt;em&gt;one&lt;/em&gt; version of mesa on most Ubuntu desktop systems. Nowadays, a fair
number of applications, including some vaguely important ones like Firefox, are
shipped as snaps, at least partly in order to alleviate the aforementioned
tension between stable distros and desktop users demanding the latest and
greatest&amp;nbsp;versions.&lt;/p&gt;
&lt;p&gt;There are many ways to handle distributing the &amp;#8220;latest&amp;#8221; versions of things on a
stable distro, but one of the things that must be dealt with is the conflict
between the versions of libraries on the stable distro, and the libraries that
the &amp;#8220;latest&amp;#8221; thing&amp;nbsp;expects.&lt;/p&gt;
&lt;p&gt;One method is to simply &amp;#8220;bundle everything&amp;#8221;; include all the libraries that
your app expects within its package. Obviously this results in a rather bloated
package which isn&amp;#8217;t going to share any libraries with anything else on the
system, and thus takes more time to load and more memory to run. It also means
dealing with all the path nonsense to ensure your application only loads the
versions of libraries from its own location. This doesn&amp;#8217;t solve &lt;em&gt;all&lt;/em&gt; the
bundling issues, but it does solve most and it is at least&amp;nbsp;simple.&lt;/p&gt;
&lt;p&gt;Another method is &amp;#8220;statically compile everything&amp;#8221;. This is a close cousin to
bundling everything, and still results in a fairly huge download but a little
slimmer than bundling all the libraries (and still shares basically nothing at
runtime). This is becoming more common with the rise of things like go and
rust, which compile this way by default &lt;a class="footnote-reference" href="#no-better" id="footnote-reference-3"&gt;[3]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Snaps take a rather interesting (and different) approach&amp;nbsp;here.&lt;/p&gt;
&lt;p&gt;All snaps are built with a &amp;#8220;base&amp;#8221; snap which represents a stable release of
Ubuntu (core22 represents a jammy base, core24 a noble base, and so on). The
&amp;#8220;base&amp;#8221; snap provides the &amp;#8220;common&amp;#8221; libraries that the snap would expect to find
on such a stable release of&amp;nbsp;Ubuntu.&lt;/p&gt;
&lt;p&gt;The snap can still statically compile, or bundle, whatever it likes, but this
means at least &lt;em&gt;some&lt;/em&gt; of its dependencies can be shared (from the base snap).
Moreover, the method by which snaps are mounted means that multiple versions of
these core snaps can co-exist so it&amp;#8217;s fine to be running a snap based on
core22, and another based on core24 (obviously they won&amp;#8217;t be sharing their core
libraries in this case, but that&amp;#8217;s the&amp;nbsp;trade-off).&lt;/p&gt;
&lt;p&gt;The libraries the core snaps include are fairly limited and, notably, &lt;em&gt;don&amp;#8217;t&lt;/em&gt;
include mesa. However, there are also &amp;#8220;content&amp;#8221; snaps that provide commonly
used sets of libraries above and beyond the core snaps. One of these is the
&amp;#8220;gnome&amp;#8221; content snap which provides (surprise surprise), Gnome, &lt;span class="caps"&gt;GTK&lt;/span&gt;, and, you
guessed it, mesa. The gnome content snap itself comes in several versions to
accomodate different &amp;#8220;base&amp;#8221;&amp;nbsp;snaps:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://snapcraft.io/gnome-42-2204"&gt;gnome-42-2204&lt;/a&gt; is the gnome content snap using core22 (jammy) as a&amp;nbsp;base&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://snapcraft.io/gnome-46-2404"&gt;gnome-46-2404&lt;/a&gt; is the gnome content snap using core24 (noble) as a&amp;nbsp;base&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now… guess which gnome content snap Firefox is (still, at the time of writing)&amp;nbsp;using?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="jammy"&gt;
&lt;h3&gt;Jammy&lt;/h3&gt;
&lt;p&gt;Shortly after the middle of October, and the noble &lt;span class="caps"&gt;SRU&lt;/span&gt; landing, I noticed we
had another problem. The noble daily would happily boot to the login prompt on
the new D0 Pi 5, and I could login and run many things. But not Firefox.
Starting that, or anything else on the core22 snap, froze the entire desktop.
Not&amp;nbsp;good.&lt;/p&gt;
&lt;p&gt;So, back to the &lt;span class="caps"&gt;SRU&lt;/span&gt; queue. The original ticket (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/mesa/+bug/2082072"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #2082072&lt;/a&gt;) was
re-targeted for jammy, the upload was made, and a bunch of other packages
regressed. Damn! Various retries later and the package was ready for&amp;nbsp;verification.&lt;/p&gt;
&lt;p&gt;But… now we had a problem. This was only an update to the mesa &lt;em&gt;deb&lt;/em&gt; package,
in Ubuntu jammy (22.04). This wasn&amp;#8217;t an update to the gnome content snap; that
would have to be done &lt;em&gt;after&lt;/em&gt; verification of the &lt;span class="caps"&gt;SRU&lt;/span&gt;. But the Pi 5 was
fundamentally &lt;em&gt;not supported&lt;/em&gt; in&amp;nbsp;jammy.&lt;/p&gt;
&lt;p&gt;Rather than trying to get a jammy image into a state it could boot (and run
Gnome) on a Pi 5, I opted to rebuild the gnome content snap locally to verify
things instead. This took a few days to work out as it&amp;#8217;s an almost entirely
undocumented process and I had to figure out that the gnome-sdk snap needed
rebuilding first (i.e. the chain goes: mesa deb -&amp;gt; gnome-sdk snap -&amp;gt; gnome&amp;nbsp;snap).&lt;/p&gt;
&lt;p&gt;To cut a long story short (or at least… less long), eventually the verification
was done, and the jammy mesa update was&amp;nbsp;released.&lt;/p&gt;
&lt;pre class="code text literal-block"&gt;
mesa (23.2.1-1ubuntu3.1~22.04.3) jammy; urgency=medium

  [ Timo Aaltonen ]
  * Add support for Pi 2712D0 stepping (LP: #2082072)

  [ Alessandro Astone ]
  * patches: Backport patch for green artifacting and GPU crash on
    radeonsi with kernel &amp;gt;= 6.10 (LP: #2083538)
&lt;/pre&gt;
&lt;p&gt;Phasing was then completed, then the gnome-sdk snap was rebuilt, then gnome
content snap, and &lt;em&gt;finally&lt;/em&gt; Firefox was working on the D0&amp;nbsp;stepping.&lt;/p&gt;
&lt;p&gt;By the time all this was done, it was the 31st of Jan. To give some context,
the .2 release of noble was scheduled for the 13th of Feb, and I was adamant
that we had to get the mesa fix in for that otherwise we&amp;#8217;d have new Pi 5 owners
unable to run Firefox &amp;#8220;out of the box&amp;#8221; on our current &lt;span class="caps"&gt;LTS&lt;/span&gt;. You could argue:
&amp;#8220;users can still login, they can always get the release from updates?&amp;#8221;.
However, the app-store is &lt;em&gt;also&lt;/em&gt; a core22 snap, and hence &lt;em&gt;also crashed the
desktop&lt;/em&gt;. While there was still the &lt;tt class="docutils literal"&gt;sudo snap refresh&lt;/tt&gt; route, I&amp;#8217;m loathe to
tell desktop users they &lt;em&gt;have&lt;/em&gt; to use the command&amp;nbsp;line.&lt;/p&gt;
&lt;p&gt;Suffice to say, it was butt-clenching time in the run up to noble&amp;#8217;s .2&amp;nbsp;release.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="a-heavy-chain-of-dependencies"&gt;
&lt;h2&gt;A heavy chain (of&amp;nbsp;dependencies)&lt;/h2&gt;
&lt;p&gt;What&amp;#8217;s the moral of the&amp;nbsp;story?&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not entirely sure. Half the problem is that everything above has &lt;em&gt;good
reason&lt;/em&gt; for being the way it is. The &lt;span class="caps"&gt;SRU&lt;/span&gt; process is long and complicated
because it&amp;#8217;s borne of the pain of unexpected regressions. It is the way it is,
because the alternative is actually more&amp;nbsp;painful.&lt;/p&gt;
&lt;p&gt;The snap eco-system is the way it is to avoid the more painful issues with
bundling absolutely&amp;nbsp;everything.&lt;/p&gt;
&lt;p&gt;Still, what we have here is a dependency chain with &lt;em&gt;many&lt;/em&gt; links, and I can&amp;#8217;t
help but think some of those links &lt;em&gt;may&lt;/em&gt; be unnecessary (or at least warrant
some&amp;nbsp;consideration).&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Should a content snap rely on another snap (the gnome-sdk snap in the middle
of the chain,&amp;nbsp;here)?&lt;/li&gt;
&lt;li&gt;Should the gnome content snap rely on the jammy debs at all, or should it be
able to pull in patches independently of&amp;nbsp;them?&lt;/li&gt;
&lt;li&gt;Should we mandate that seeded snaps always have (useful) contact information
and well-documented procedures for&amp;nbsp;building?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, there are trade-offs in every direction here. At the moment, the
gnome content snap can reasonably rely on the stability (and quality)
guarantees of the underlying deb. Do we really want to move away from that?
Should we be pushing seeded applications to use later base snaps, so that SRUs
don&amp;#8217;t have to go back as far in&amp;nbsp;time?&lt;/p&gt;
&lt;p&gt;I don&amp;#8217;t know the answers to all this yet. But I do know taking 3+ months to get
a fix to the place it needs to be is no&amp;nbsp;fun!&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="new-dts" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This is categorically not true. New device-trees were also added
and probably a whole host of other changes I haven&amp;#8217;t noticed, but this post
is going to be waffley enough without me going off track with every detail!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="paranoid" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I originally wrote &amp;#8220;paranoia of the sys-admin&amp;#8221;, but frankly it&amp;#8217;s
&lt;em&gt;not&lt;/em&gt; paranoia. This level of risk-aversion is usually the result of
hard-learned lessons!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="no-better" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Which is not to say I think it&amp;#8217;s any better. Both static
compilation and bundling of everything do &amp;#8220;challenge&amp;#8221; the security / bug-fix
model of classic distributions by essentially ignoring updates to some / all
base libraries. Provided they actually stay &lt;em&gt;ahead&lt;/em&gt; of those base libraries,
that&amp;#8217;s not an issue…&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="noble"></category><category term="oracular"></category><category term="snaps"></category><category term="d0"></category></entry><entry><title>Loading the Deck</title><link href="https://waldorf.waveform.org.uk/2025/loading-the-deck.html" rel="alternate"></link><published>2025-02-27T00:00:00+00:00</published><updated>2025-02-27T23:00:36+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2025-02-27:/2025/loading-the-deck.html</id><summary type="html">&lt;p class="first last"&gt;A cautionary tale of &lt;span class="caps"&gt;SD&lt;/span&gt; cards and online&amp;nbsp;reviews&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I mentioned in my &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/dealing-the-sd-cards.html"&gt;last post&lt;/a&gt; on this topic that fake &lt;span class="caps"&gt;SD&lt;/span&gt; cards have been an
issue in the&amp;nbsp;past.&lt;/p&gt;
&lt;blockquote&gt;
Buy from well-known outlets, not minor vendors on whatever variant of
flea-bay you frequent. Just because it’s on Amazon, doesn’t mean it’s not a
knock-off; check the actual seller.&lt;/blockquote&gt;
&lt;p&gt;For that post, I also purchased a variety of &lt;span class="caps"&gt;SD&lt;/span&gt; cards of various models for
testing purposes, some from Amazon. I also&amp;nbsp;stated…&lt;/p&gt;
&lt;blockquote&gt;
I &lt;em&gt;haven&amp;#8217;t&lt;/em&gt; had any issues for the last few years, but during them I&amp;#8217;ve
stuck to buying strictly from either SanDisk or Samsung [&amp;#8230;]&lt;/blockquote&gt;
&lt;p&gt;Guess how that&amp;#8217;s&amp;nbsp;going!&lt;/p&gt;
&lt;div class="section" id="another-one-bites-the-dust"&gt;
&lt;h2&gt;Another One Bites the&amp;nbsp;Dust&lt;/h2&gt;
&lt;p&gt;Another month rolls by, and another Ubuntu release rolls off the production
line. In this case it was Noble&amp;#8217;s .2 release&amp;nbsp;(24.04.2).&lt;/p&gt;
&lt;p&gt;At each release, we go through a round of so-called &amp;#8220;&lt;span class="caps"&gt;ISO&lt;/span&gt; tests&amp;#8221; &lt;a class="footnote-reference" href="#iso" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. This
invariably involves grabbing a handful of &lt;span class="caps"&gt;SD&lt;/span&gt; cards, flashing the release
candidate image to them, booting them on various models of Pi model and running
through a &lt;a class="reference external" href="https://iso.qa.ubuntu.com/"&gt;battery of tests&lt;/a&gt;. Once a board has passed or failed, pull the
card, reflash, bung it in another board, rinse and&amp;nbsp;repeat.&lt;/p&gt;
&lt;p&gt;This means the life of an &lt;span class="caps"&gt;SD&lt;/span&gt; card in my possession boils down to one of two&amp;nbsp;possibilities:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;If it&amp;#8217;s lucky, it gets stuck in a &amp;#8220;long running&amp;#8221; Pi serving something in my
house. This means 24x7 duty for many years, but usually relatively light&amp;nbsp;duty.&lt;/li&gt;
&lt;li&gt;If it&amp;#8217;s unlucky, it goes in the drawer and for much of the year it stays
there. But, before each release, it&amp;#8217;ll get hauled out and for a few days be
brutally tortured with tens of gigabytes written to it over and over&amp;nbsp;again.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If I have a card fail on me, it&amp;#8217;s inevitably during a release. This time was no
different, and I had a fancy gold-coloured &amp;#8220;SanDisk Extreme&amp;#8221; die. Rather than
one of the usual failure modes (fails to read entirely, or silently ignores
writes), this one wrote the candidate image happily but then reported tons of
I/O errors during boot. A little odd, but not&amp;nbsp;unprecedented.&lt;/p&gt;
&lt;p&gt;What was strange was that this card was barely more than 1½ years old. This is
an unusually short life-span, in my experience. Brutal though I am to these
test cards, they usually last 4 or more years. There was no time to investigate
during the release, so I tossed it in the &amp;#8220;dead cards&amp;#8221; drawer and moved on.
However, the release is now done, and I&amp;#8217;ve taken another&amp;nbsp;look.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="aaaa"&gt;
&lt;h2&gt;Aaaa!&lt;/h2&gt;
&lt;p&gt;When you insert an &lt;span class="caps"&gt;SD&lt;/span&gt; card into a Pi, if you&amp;#8217;re watching the kernel log (and I
often am in some window) you&amp;#8217;ll typically see something like the following&amp;nbsp;appear:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
[344239.702588] mmc0: cannot verify signal voltage switch
[344239.771565] mmc0: new ultra high speed SDR104 SDHC card at address 59b4
[344239.771898] mmcblk0: mmc0:59b4 USD00 29.5 GiB
[344239.773131]  mmcblk0: p1 p2
[344239.773418] mmcblk0: mmc0:59b4 USD00 29.5 GiB
&lt;/pre&gt;
&lt;p&gt;What does all this&amp;nbsp;mean?&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;mmc0: cannot verify signal voltage&amp;nbsp;switch&lt;/dt&gt;
&lt;dd&gt;Basically noise; just a warning that the signal voltage was switched but we
can&amp;#8217;t be sure it was successful (hardware&amp;nbsp;limitation).&lt;/dd&gt;
&lt;dt&gt;mmc0: new ultra high speed &lt;span class="caps"&gt;SDR104&lt;/span&gt; &lt;span class="caps"&gt;SDHC&lt;/span&gt; card at address&amp;nbsp;59b4&lt;/dt&gt;
&lt;dd&gt;The interface has noticed a new &lt;em&gt;ultra high speed&lt;/em&gt; (ooooh!) card which
supports &lt;span class="caps"&gt;SDR104&lt;/span&gt; signalling at &amp;#8220;address 59b4&amp;#8221;. Typically an interface only
has a single card slot, but technically it can support several. This is the
&amp;#8220;address&amp;#8221; of the card within that&amp;nbsp;interface.&lt;/dd&gt;
&lt;dt&gt;mmcblk0: mmc0:59b4 &lt;span class="caps"&gt;USD00&lt;/span&gt; 29.5&amp;nbsp;GiB&lt;/dt&gt;
&lt;dd&gt;Mostly a repeat of the prior information. This says the kernel&amp;#8217;s created a
new &amp;#8220;mmcblk0&amp;#8221; device representing the card with address &amp;#8220;59b4&amp;#8221; on the
&amp;#8220;mmc0&amp;#8221; interface, and it&amp;#8217;s about 29.&lt;span class="caps"&gt;5GB&lt;/span&gt; in&amp;nbsp;capacity…&lt;/dd&gt;
&lt;dt&gt;mmcblk0: p1&amp;nbsp;p2&lt;/dt&gt;
&lt;dd&gt;… and it&amp;#8217;s got two&amp;nbsp;partitions.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Incidentally, the above is from a Raspberry Pi branded &lt;span class="caps"&gt;SD&lt;/span&gt; card. Let&amp;#8217;s see what
the broken SanDisk Extreme&amp;nbsp;reports:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
[341496.190074] mmc0: cannot verify signal voltage switch
[341496.266122] mmc0: new ultra high speed SDR104 SDXC card at address aaaa
[341496.266891] mmcblk0: mmc0:aaaa SN64G 59.5 GiB
[341496.268624]  mmcblk0: p1 p2
[341496.269098] mmcblk0: mmc0:aaaa SN64G 59.5 GiB (quirks 0x00004000)
&lt;/pre&gt;
&lt;p&gt;Huh. That &amp;#8220;aaaa&amp;#8221; address looks a bit&amp;nbsp;odd.&lt;/p&gt;
&lt;p&gt;This may be complete coincidence, but having come across fakes in the past,
sometimes dodgy cards have &amp;#8220;suspicious&amp;#8221; (read: low entropy) fields in their
registers. Serial numbers filled with zeros and the like. Querying other things
(&lt;span class="caps"&gt;CID&lt;/span&gt;, manufacturers &lt;span class="caps"&gt;ID&lt;/span&gt;, etc) under &lt;tt class="docutils literal"&gt;/sys/block/mmcblk0/device&lt;/tt&gt; yields nothing
terribly suspicious here, but nonetheless, this feels a little odd so let&amp;#8217;s dig
a bit&amp;nbsp;further.&lt;/p&gt;
&lt;p&gt;Rather than throwing my dead cards in the bin, in recent years I&amp;#8217;ve taken to
keeping them in a drawer. Most still have &lt;em&gt;some&lt;/em&gt; function, and I wondered if I
could spot any patterns in their failure&amp;nbsp;modes.&lt;/p&gt;
&lt;p&gt;In the drawer I&amp;#8217;ve got a &amp;#8220;SanDisk Ultra&amp;#8221; card (this is their &amp;#8220;bog standard&amp;#8221;
range with grey rather than gold colouring on the case) which failed a year ago
after a suspiciously short life-span. What does it report on&amp;nbsp;insertion?&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
[341732.375548] mmc0: cannot verify signal voltage switch
[341732.448272] mmc0: new ultra high speed SDR104 SDHC card at address aaaa
[341732.448560] mmcblk0: mmc0:aaaa SD32G 29.7 GiB
[341732.450888]  mmcblk0: p1 p2
[341732.451108] mmcblk0: mmc0:aaaa SD32G 29.7 GiB (quirks 0x00004000)
&lt;/pre&gt;
&lt;p&gt;Hmm. Another &amp;#8220;aaaa&amp;#8221;. Still, maybe that&amp;#8217;s just the address that SanDisk cards
use generally? Let&amp;#8217;s try another SanDisk Ultra branded card which has so far
worked&amp;nbsp;reliably:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
[345859.140220] mmc0: cannot verify signal voltage switch
[345859.209840] mmc0: new ultra high speed SDR104 SDXC card at address 59b4
[345859.210384] mmcblk0: mmc0:59b4 SD64G 59.4 GiB
[345859.212228]  mmcblk0: p1 p2
[345859.212795] mmcblk0: mmc0:59b4 SD64G 59.4 GiB (quirks 0x00004000)
&lt;/pre&gt;
&lt;p&gt;So, the &amp;#8220;aaaa&amp;#8221; address appears common to two cards both purportedly from
SanDisk, both of which failed suspiciously&amp;nbsp;early.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="aaaamazon"&gt;
&lt;h2&gt;Aaaamazon&lt;/h2&gt;
&lt;p&gt;Being more curious now I went and dug out my order history to see when and
where these were purchased. I&amp;#8217;m not 100% certain of which batch the &lt;span class="caps"&gt;32GB&lt;/span&gt;
&amp;#8220;Ultra&amp;#8221; card came in. However, I can say with confidence that both were
purchased from Amazon, both from the &amp;#8220;SanDisk Store&amp;#8221; on there, and at least 6
months elapsed between the purchase of the &lt;span class="caps"&gt;32GB&lt;/span&gt; &amp;#8220;Ultra&amp;#8221; card (latest February
2023) and the &lt;span class="caps"&gt;64GB&lt;/span&gt; &amp;#8220;Extreme&amp;#8221; card (July&amp;nbsp;2023).&lt;/p&gt;
&lt;p&gt;What to&amp;nbsp;do?&lt;/p&gt;
&lt;p&gt;Well, not much. It&amp;#8217;s another fake from Amazon (which more and more resembles an
online tat-store in my experience). Still, I figured in the interests of being
a conscientious consumer, I should leave a review cautioning others that even
buying from the official &amp;#8220;SanDisk Store&amp;#8221; is not a cast-iron guarantee of
quality. I spent 5 minutes throwing together a quick précis of the above (&amp;#8220;aaaa
reported by kernel&amp;#8221;, &amp;#8220;substantially shorter lifespan than other SanDisk cards&amp;#8221;,
etc.&amp;nbsp;etc.)&lt;/p&gt;
&lt;p&gt;And I would&amp;#8217;ve left it at that. But for the email that just&amp;nbsp;arrived…&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please edit and resubmit your&amp;nbsp;review&lt;/p&gt;
&lt;p&gt;Hello Dave Jones, We could not post your review because it does not meet
our community guidelines. Please edit and resubmit your review. Before you
do, make sure it meets all of our&amp;nbsp;guidelines.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Intriguing. I wonder which &amp;#8220;community guidelines&amp;#8221; I&amp;#8217;ve violated. Unfortunately,
while they&amp;#8217;ve linked to their guidelines (which are pretty much as one would
expect) they haven&amp;#8217;t specified which one I violated, and the review is
apparently gone. So I&amp;#8217;ve no&amp;nbsp;clue.&lt;/p&gt;
&lt;p&gt;Naturally, this leaves me feeling less-than-confident in the positively stellar
review ratings alongside this&amp;nbsp;product.&lt;/p&gt;
&lt;img alt="" class="align-center" src="https://waldorf.waveform.org.uk/images/sd-card-reviews.png" /&gt;
&lt;p&gt;Though, again, hilariously skewed reviews are hardly a new feature of Amazon
either. More importantly: can I still stand by my former advice? Probably&amp;nbsp;not.&lt;/p&gt;
&lt;blockquote&gt;
eBay, Amazon, Alibaba, whoever. If it&amp;#8217;s a large marketplace, I&amp;#8217;ve had fakes
from it (certainly from the aforementioned big three). Ideally, buy from
your local electronics vendor (&lt;a class="reference external" href="https://shop.pimoroni.com/products/microsd-card-with-raspberry-pi-os?variant=31703694245971"&gt;Pimoroni&lt;/a&gt; or &lt;a class="reference external" href="https://thepihut.com/products/sandisk-microsd-card-class-10-a1"&gt;The Pi Hut&lt;/a&gt; for me)
&lt;span class="strike"&gt;but failing that, look for the larger vendors on the big
marketplaces&lt;/span&gt;.&lt;/blockquote&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="iso" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="caps"&gt;ISO&lt;/span&gt; because this used to be limited to &lt;span class="caps"&gt;ISO&lt;/span&gt; &lt;span class="caps"&gt;CD&lt;/span&gt; images. Though we still
make &lt;span class="caps"&gt;ISO&lt;/span&gt; images for various platforms, we&amp;#8217;re just testing raw disk images
for the Pi.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="sd"></category><category term="reliability"></category><category term="amazon"></category></entry><entry><title>How Noble in reason</title><link href="https://waldorf.waveform.org.uk/2025/how-noble-in-reason.html" rel="alternate"></link><published>2025-02-02T00:00:00+00:00</published><updated>2025-02-02T17:22:03+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2025-02-02:/2025/how-noble-in-reason.html</id><summary type="html">&lt;p class="first last"&gt;A look at the trials and tribulations of the current &lt;span class="caps"&gt;LTS&lt;/span&gt; release,
Noble Numbat (24.04 &lt;span class="caps"&gt;LTS&lt;/span&gt;)&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Noble&amp;#8217;s release is &lt;span class="strike"&gt;coming up&lt;/span&gt; (as I edit this, it&amp;#8217;s now many months in
the past and even noble&amp;#8217;s .1, and oracular&amp;#8217;s releases are long gone), so it&amp;#8217;s
high time I write something about this most difficult of births. Still, you may
have to forgive my use of the future&amp;nbsp;tense…&lt;/p&gt;
&lt;div class="section" id="welcome-back-pointers"&gt;
&lt;h2&gt;Welcome back,&amp;nbsp;pointers&lt;/h2&gt;
&lt;p&gt;What&amp;#8217;s new? For Pi users the big news is that this is an &lt;abbr title="Long Term Service"&gt;&lt;span class="caps"&gt;LTS&lt;/span&gt;&lt;/abbr&gt;, and it&amp;#8217;ll be the first Ubuntu &lt;span class="caps"&gt;LTS&lt;/span&gt; that supports the new
hardware of the day: the Pi 5. For those not familiar, that means 5 years of
free support (and &lt;a class="reference external" href="https://ubuntu.com/pro"&gt;another 5 paid&lt;/a&gt;, or free at various tiers of&amp;nbsp;usage).&lt;/p&gt;
&lt;p&gt;All well and good, but what&amp;#8217;s new in the software itself? Not quite as much as
I&amp;#8217;d hoped. The final part of my series on &lt;span class="caps"&gt;NBD&lt;/span&gt; (&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2024/i-aint-got-nbd.html"&gt;part 3&lt;/a&gt; following on from &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/nbd-knows.html"&gt;part 1&lt;/a&gt;, &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/nbd-does-it-better.html"&gt;part 2&lt;/a&gt;)
won&amp;#8217;t operate until the &lt;span class="strike"&gt;first&lt;/span&gt; second point release (24.04.2)
&lt;a class="footnote-reference" href="#maybe" id="footnote-reference-1"&gt;[1]&lt;/a&gt; scheduled for February &lt;span class="strike"&gt;next&lt;/span&gt; this year, as I didn&amp;#8217;t manage
to shepherd the nbd-client package through into main in time. Sadly, there
wasn&amp;#8217;t time to address libcamera either and so, at present, it&amp;#8217;s completely
broken and won&amp;#8217;t even list the Pi&amp;#8217;s camera modules. This will be a priority to
fix in the &lt;span class="strike"&gt;next (oracular)&lt;/span&gt; next +1 (plucky) cycle (for eventual &lt;span class="caps"&gt;SRU&lt;/span&gt;
to noble). In fact, those are just the tip of a rather long list of stuff I
need to&amp;nbsp;fix.&lt;/p&gt;
&lt;p&gt;But enough doom and gloom. One piece of very good news is that the Firefox snap
(or more precisely the &lt;span class="caps"&gt;GNOME&lt;/span&gt; content snap) finally got updated to mesa version
23.2. This brings full hardware acceleration on the Pi 5 to the browser, so
instead of the hilariously pathetic 2fps we got with the classic &lt;a class="reference external" href="https://webglsamples.org/aquarium/aquarium.html"&gt;Aquarium&lt;/a&gt;
benchmark in mantic, it&amp;#8217;s more like &lt;span class="strike"&gt;40-60fps&lt;/span&gt; 30-40fps in noble (I
managed 60fps at 1080p full-screen when first testing this pre-release, but by
the time we got to release it seems something else in mesa changed and we were
back down to 30-40 &amp;#8212; still, better than&amp;nbsp;2!).&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;#8217;s also been updated to version 46. Amongst many changes, this fixed a
&lt;a class="reference external" href="https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/753"&gt;long standing&lt;/a&gt; and, as we&amp;#8217;ll see, quite pertinent issue with its
notifications. As you may have noticed in prior versions, desktop notifications
could have &amp;#8220;actions&amp;#8221; associated with them which would appear as buttons beneath
the notification. For example, when plugging in a storage device the action on
the associated notification would be &amp;#8220;Open with&amp;nbsp;Files&amp;#8221;.&lt;/p&gt;
&lt;img alt="A desktop notification showing a new &amp;quot;69GB volume&amp;quot; has been mounted, with an action button at the bottom labelled &amp;quot;Open with Files&amp;quot;" class="align-center" src="https://waldorf.waveform.org.uk/images/desktop-notify-jammy.png" /&gt;
&lt;p&gt;But if you happened to miss the notification, and it wound up in the tray, the
actions were&amp;nbsp;gone…&lt;/p&gt;
&lt;img alt="The &amp;quot;69GB volume&amp;quot; desktop notification from earlier, but now in the backlog tray, missing the action button" class="align-center" src="https://waldorf.waveform.org.uk/images/desktop-notify-backlog-jammy.png" /&gt;
&lt;p&gt;As the linked ticket showed, this has been a pretty long standing bug in the
desktop, but &lt;span class="caps"&gt;GNOME&lt;/span&gt; 46 finally squashes it, and just in time for one of the new
Pi-specific bits in noble. The Pi 5&amp;#8217;s new &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Power_management_integrated_circuit"&gt;&lt;span class="caps"&gt;PMIC&lt;/span&gt;&lt;/a&gt; includes the ability to
inform users when the previous reboot was caused by an undervolt (a &amp;#8220;brownout&amp;#8221;
because the power-supply isn&amp;#8217;t up to snuff). The RaspiOS desktop helpfully
informs users via a desktop notification when this happens, and now so does&amp;nbsp;Ubuntu.&lt;/p&gt;
&lt;img alt="A desktop notification from the &amp;quot;Raspberry Pi PMIC Monitor&amp;quot; indicating &amp;quot;This power supply is not capable of supplying 5A; power to peripherals will be restricted&amp;quot;. Beneath the warning are two action buttons: &amp;quot;More information&amp;quot;, and &amp;quot;Don't show again&amp;quot;" class="align-center" src="https://waldorf.waveform.org.uk/images/desktop-notify-noble.png" /&gt;
&lt;p&gt;As you can see, the Ubuntu version also includes actions to silence the
warning, and to get more information. But when the warning disappears into the
system tray, with previous versions of &lt;span class="caps"&gt;GNOME&lt;/span&gt; there were no actions and, worse
still, the notification was truncated. Now you can expand it in the tray to see
everything you&amp;nbsp;need.&lt;/p&gt;
&lt;img alt="The desktop notification from earlier about the power supply, but now in the backlog tray with a truncated message and no action buttons. However, an expansion button is now present in the top right" class="align-center" src="https://waldorf.waveform.org.uk/images/desktop-notify-backlog-noble.png" /&gt;
&lt;img alt="The desktop notification from earlier about the power supply, but expanded to show the full message, and the two action buttons, &amp;quot;More information&amp;quot; and &amp;quot;Don't show again&amp;quot;" class="align-center" src="https://waldorf.waveform.org.uk/images/desktop-notify-expanded-noble.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="what-a-piece-of-work"&gt;
&lt;h2&gt;What a piece of&amp;nbsp;work…&lt;/h2&gt;
&lt;p&gt;Another major piece of news is a new official flavour of Ubuntu for the
Raspberry Pi: Edubuntu have spun up a &lt;a class="reference external" href="https://cdimages.ubuntu.com/edubuntu/releases/24.04.1/release/"&gt;pre-installed Raspberry Pi desktop
image&lt;/a&gt;. There are a wealth of applications pre-installed here, including
several I was previously unaware of like &lt;a class="reference external" href="https://marble.kde.org/"&gt;Marble&lt;/a&gt;, a fantastic globe explorer
including some wonderful ancient map modes (ah, the island of California!),
&lt;a class="reference external" href="https://apps.kde.org/en-gb/kig/"&gt;Kig&lt;/a&gt;, a constructive geometry application, and &lt;a class="reference external" href="https://sgimenez.github.io/laby/"&gt;laby&lt;/a&gt;, a very nice gamified
coding tutor supporting several&amp;nbsp;languages.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="how-infinite-in-faculty"&gt;
&lt;h2&gt;How infinite in&amp;nbsp;faculty&lt;/h2&gt;
&lt;p&gt;The other big news is that, while armhf (32-bit &lt;span class="caps"&gt;ARM&lt;/span&gt;) as an architecture is
still supported for 24.04&amp;#8217;s lifetime, we will &lt;em&gt;not&lt;/em&gt; be producing Raspberry Pi
images for it. This means there is &lt;em&gt;no&lt;/em&gt; upgrade path for users currently on
(for example) 22.04 armhf images. Those users will need to re-image with the
arm64&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;There are multiple reasons here, but the primary reason is a combination of the
Pi 5&amp;#8217;s limited support for 32-bit architecture, and a difference in the way
RaspiOS and Ubuntu support multiple models of Raspberry Pi with a small number
of&amp;nbsp;images.&lt;/p&gt;
&lt;p&gt;Traditionally (and even today in some parts of the &lt;abbr title="Single Board Computer"&gt;&lt;span class="caps"&gt;SBC&lt;/span&gt;&lt;/abbr&gt; space), &lt;span class="caps"&gt;OS&lt;/span&gt; vendors produced an &lt;span class="caps"&gt;OS&lt;/span&gt; image &lt;em&gt;per board&lt;/em&gt;. Imagine this in
the context of Raspberry Pi. There would be an image for the Raspberry Pi 2B,
another for the Raspberry Pi 3B, possibly another for the 3B+, certainly
another for the 4B, and so on, each tailored to their specific board. There
might be further variations on top of this, like armhf and arm64 images for the
3B, two more for the 4B, maybe different desktop variants, the list goes&amp;nbsp;on.&lt;/p&gt;
&lt;p&gt;The result would be an immensely confusing proliferation of images, with the
user having to know exactly which board they had, and typically not being able
to move eminently moveable storage (like an &lt;span class="caps"&gt;SD&lt;/span&gt; card) between&amp;nbsp;boards.&lt;/p&gt;
&lt;p&gt;RaspiOS (or Raspbian formerly) was quite the pioneer here. Early on, as the Pi
family grew from the B+ to the 2B and beyond, it was recognized that asking
teachers (particularly, given the educational focus) to know exactly which
board(s) they had, and figure out which image they needed to download, was
simply not practical. This is why there&amp;#8217;s still a single RaspiOS image that
works on absolutely all Raspberry Pi boards: keep it simple for the&amp;nbsp;user.&lt;/p&gt;
&lt;p&gt;Obviously there&amp;#8217;s been some proliferation (a lite variant without a desktop,
and architecture variations), and the situation of having a single image that
works universally probably can&amp;#8217;t last forever, but ultimately the goal of
minimizing the number of images is still&amp;nbsp;sought.&lt;/p&gt;
&lt;p&gt;What about&amp;nbsp;Ubuntu?&lt;/p&gt;
&lt;p&gt;When I first joined the Foundations team several (nearly six?) years ago, the
&amp;#8220;image per board&amp;#8221; model was being followed. There was an Ubuntu image for the
2B, another for the 3B, and they were thinking about another for the (at the
time, forthcoming) 4B &lt;a class="footnote-reference" href="#no-b" id="footnote-reference-2"&gt;[2]&lt;/a&gt;, assuming they could drop the 2B image in due
course as it ran out of&amp;nbsp;support.&lt;/p&gt;
&lt;p&gt;The first thing I had to correct was the notion that the 2B was going to drop
out of support anytime soon. Raspberry Pi have always promised 10 years of
production on all models, and so far they&amp;#8217;ve not disappointed: as of writing
this the 2B is &lt;em&gt;still&lt;/em&gt; &lt;a class="reference external" href="https://www.raspberrypi.com/products/raspberry-pi-2-model-b/"&gt;available&lt;/a&gt;. This also meant that following the &amp;#8220;image per
board&amp;#8221; model was completely unsustainable. The resulting number of images would
be insane (not to mention the support burden). So I switched Ubuntu to a
&amp;#8220;minimal images&amp;#8221; model, specifically one image for armhf and another arm64
&lt;a class="footnote-reference" href="#no-desktop" id="footnote-reference-3"&gt;[3]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;How?&lt;/p&gt;
&lt;p&gt;While RaspiOS and Ubuntu both follow the &amp;#8220;minimal images&amp;#8221; model, we both do so
in quite different ways. What matters here is the separation between &amp;#8220;kernel&amp;#8221;
and &amp;#8220;userland&amp;#8221;, and what architectures are supported by each SoC. In userland,
things are relatively&amp;nbsp;simple:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="49%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="17%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;SoC&lt;/th&gt;
&lt;th class="head"&gt;ARMv6&lt;/th&gt;
&lt;th class="head"&gt;ARMv7&lt;/th&gt;
&lt;th class="head"&gt;ARMv8&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;2835
(Pi 1, Pi Zero)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2836
(Pi 2)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2837
(Pi 3, 3+, Zero 2)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2711
(Pi 4, 400)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2712
(Pi 5, 500)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As you can see, ARMv6 is a &amp;#8220;universal&amp;#8221; architecture for userland, so compile
everything except the kernel for ARMv6 and your image will work on all boards.
Great! But what about the&amp;nbsp;kernel&amp;#8230;&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="49%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="17%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;SoC&lt;/th&gt;
&lt;th class="head"&gt;ARMv6&lt;/th&gt;
&lt;th class="head"&gt;ARMv7&lt;/th&gt;
&lt;th class="head"&gt;ARMv8&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;2835
(Pi 1, Pi Zero)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2836
(Pi 2)&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2837
(Pi 3, 3+, Zero 2)&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2711
(Pi 4, 400)&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2712
(Pi 5, 500)&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Now things are more complicated. For a truly universal image, you require
separate kernels. This is the route RaspiOS has taken on their 32-bit images.
The userland is ARMv6, but there are four &lt;a class="footnote-reference" href="#whyfour" id="footnote-reference-4"&gt;[4]&lt;/a&gt; separate kernels on the
image, with the bootloader selecting the appropriate one for the&amp;nbsp;board.&lt;/p&gt;
&lt;p&gt;At Ubuntu, we don&amp;#8217;t have the resources to maintain four separate kernel builds
for the Raspberry Pi &lt;a class="footnote-reference" href="#pervendor" id="footnote-reference-5"&gt;[5]&lt;/a&gt;. However, we also never (officially)
supported the Pi 1 models. With no need for ARMv6 support, the route we took in
Ubuntu was a simpler &amp;#8220;pure architecture&amp;#8221; model: the armhf images use an ARMv7
kernel and userland; the arm64 images use an ARMv8 kernel and&amp;nbsp;userland.&lt;/p&gt;
&lt;p&gt;To be clear, I don&amp;#8217;t think either model is perfect. There are advantages,
disadvantages, and compromises all round here. This is just how things are as a
result of history and the limitations and resources surrounding each distro.
Still, the goal of minimizing the number of images is a worthwhile one, however
it is&amp;nbsp;achieved.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="good-for-you-it-s-a-growth-industry"&gt;
&lt;h2&gt;Good for you, it&amp;#8217;s a growth&amp;nbsp;industry…&lt;/h2&gt;
&lt;p&gt;There&amp;#8217;s a problem. Note that the 2712 on the Pi 5 cannot boot an ARMv7 kernel.
So with noble we were in the position of having an armhf image that could boot
on &lt;em&gt;some&lt;/em&gt; modern Pi models, but not all. The options available&amp;nbsp;were:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Retro-fit an arm64 kernel onto the armhf userland images and have the
bootloader select the right one, a bit like RaspiOS. Ultimately this was too
complex to support (several systems assume there is &amp;#8220;one kernel&amp;#8221; on the
system, not multiple that need to be kept in&amp;nbsp;lock-step)&lt;/li&gt;
&lt;li&gt;Make two armhf images, one with an armhf kernel and one with an arm64
kernel. Given this would only be of use to Pi 5 users who wanted an armhf
userland, this seemed far too niche to expend time and effort on (you&amp;#8217;ll see
just how niche in a&amp;nbsp;bit!).&lt;/li&gt;
&lt;li&gt;Make the armhf image, but explicitly state it&amp;#8217;s not supported on the Pi 5,
and likely any future Raspberry Pi&amp;nbsp;boards.&lt;/li&gt;
&lt;li&gt;Drop the armhf image, leaving it as a legacy architecture only for old&amp;nbsp;releases.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Personally, I would have preferred the third option. However, armhf has always
had minimal (and waning) interest from Ubuntu users. A few years ago, for every
one armhf download there were 5 arm64 downloads. Today, that ratio is 1:30 and&amp;nbsp;rising.&lt;/p&gt;
&lt;p&gt;Given that arm64 support covers all boards with the exception of the Raspberry
Pi 1 (which we never supported) and the Raspberry Pi 2 (which is easily
classified as a &amp;#8220;legacy&amp;#8221; board at this point, i.e. people should not be
planning &lt;em&gt;new&lt;/em&gt; products based on this board), the decision was made to simplify
things in this release, and drop armhf for the Raspberry Pi. The resulting
arm64 images available for noble (and onwards) work on every Raspberry Pi board
we&amp;#8217;ve ever supported &lt;em&gt;except&lt;/em&gt; the Pi&amp;nbsp;2.&lt;/p&gt;
&lt;p&gt;The support matrix for all models of Raspberry Pi, plotted against all
currently supported versions of Ubuntu can be found in the &lt;a class="reference external" href="https://canonical-ubuntu-boards.readthedocs-hosted.com/en/latest/how-to/raspberry-pi/"&gt;Raspberry Pi
section&lt;/a&gt; of the new &lt;a class="reference external" href="https://canonical-ubuntu-boards.readthedocs-hosted.com/en/latest/"&gt;Ubuntu Boards documentation&lt;/a&gt; project. This aims to
replace the out of date (and rather variable) Ubuntu wiki pages with a single
source of information for installing Ubuntu on all manner of single board
computers (including Raspberry Pi,&amp;nbsp;naturally).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="let-s-have-a-drink-and-forget-the-whole-damned-thing"&gt;
&lt;h2&gt;Let&amp;#8217;s have a drink and forget the whole damned&amp;nbsp;thing&lt;/h2&gt;
&lt;p&gt;To say it&amp;#8217;s been a challenging year would be an understatement of frankly comic&amp;nbsp;proportions.&lt;/p&gt;
&lt;p&gt;Between the extensive work on the solution to the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Year_2038_problem"&gt;Year 2038 problem&lt;/a&gt; (i.e.
the 64-bit time_t transition on armhf), the &lt;a class="reference external" href="https://discourse.ubuntu.com/t/noble-numbat-beta-delayed-xz-liblzma-security-update/43827"&gt;challenges&lt;/a&gt; involved in the
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/XZ_Utils_backdoor"&gt;xz-utils hack&lt;/a&gt;, some considerable churn in personnel, and the mess we found
ourselves in over the D0 stepping (more on that in the next post!), 2024 has
been … tough. Still, I&amp;#8217;m vaguely hoping 2025, and 25.04 (plucky) might be less
&amp;#8220;eventful&amp;#8221; &lt;a class="footnote-reference" href="#steve" id="footnote-reference-6"&gt;[6]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve got a &lt;a class="reference external" href="https://launchpad.net/~r41k0u"&gt;new helper&lt;/a&gt; on the Pi side of Foundations, with the result that we
&lt;em&gt;might&lt;/em&gt; actually get the camera working this cycle (at least, that&amp;#8217;s the plan).
Various long-standing architectural issues &lt;em&gt;might&lt;/em&gt; resolve this cycle (he says,
looking at the desktop installer). And there&amp;#8217;s a &lt;em&gt;fair&lt;/em&gt; chance we won&amp;#8217;t crash
into any more hardware incompatibilities until … oh, at least the middle of the&amp;nbsp;year.&lt;/p&gt;
&lt;p&gt;I&amp;nbsp;hope.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="maybe" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;We&amp;#8217;ll see if the &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/nbd/+bug/2054480"&gt;nbd &lt;span class="caps"&gt;MIR&lt;/span&gt;&lt;/a&gt; makes it in time. Pragyansh (my &lt;a class="reference external" href="https://launchpad.net/~r41k0u"&gt;new
helper&lt;/a&gt; in Ubuntu on Pi) has done fantastic work on this, but even that may
not be enough and unfortunately there are even more pressing things to get
in. But that&amp;#8217;s for the next post…&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="no-b" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;There was no image for the B+ (or prior) boards because Ubuntu never
(officially) supported the ARMv6 architecture on those boards&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="no-desktop" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Both were server images only at the time &amp;#8212; we didn&amp;#8217;t have a
desktop offering as the Ubuntu desktop was &lt;em&gt;far&lt;/em&gt; too heavy to run on the
3B+, which was the latest model at the time&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="whyfour" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;There are two ARMv7 kernels: one for 2837 boards (Pi 3 era), one
for the 2711 (Pi 4). By default, the 2711 boots the ARMv8 64-bit kernel, but
can be forced back to 32-bit for various reasons, in which case it boots the
&lt;tt class="docutils literal"&gt;kernel7l.img&lt;/tt&gt; rather than &lt;tt class="docutils literal"&gt;kernel7.img&lt;/tt&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="pervendor" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;More precisely: we maintain dozens of different kernel
variations but rarely more than one or two configurations for a specific
board or vendor&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="steve" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Well, 2025 got off to a crap start. &lt;a class="reference external" href="https://discourse.ubuntu.com/t/remembering-and-thanking-steve-langasek/52665"&gt;&lt;span class="caps"&gt;RIP&lt;/span&gt; Steve&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="noble"></category></entry><entry><title>I ain’t got NBD</title><link href="https://waldorf.waveform.org.uk/2024/i-aint-got-nbd.html" rel="alternate"></link><published>2024-12-03T00:00:00+00:00</published><updated>2024-12-03T15:52:56+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2024-12-03:/2024/i-aint-got-nbd.html</id><summary type="html">&lt;p class="first last"&gt;The final part of the &lt;span class="caps"&gt;NBD&lt;/span&gt; netboot series, where we solve our issues
with the coherence of the boot&amp;nbsp;partition&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/nbd-does-it-better.html"&gt;Last time&lt;/a&gt; we set up an &lt;abbr title="Network Block Device"&gt;&lt;span class="caps"&gt;NBD&lt;/span&gt;&lt;/abbr&gt; server, and the associated &lt;abbr title="Dynamic Host Configuration Protocol"&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt;&lt;/abbr&gt; machinery to netboot our Pi. We then discovered a
rather serious problem: when the Pi updated the content of the boot partition,
the &lt;abbr title="Trivial File Transfer Protocol"&gt;&lt;span class="caps"&gt;TFTP&lt;/span&gt;&lt;/abbr&gt; wouldn&amp;#8217;t notice and would
serve old files because the mount of the boot partition on the server didn&amp;#8217;t
realize it was &amp;#8220;shared&amp;#8221; with the &lt;span class="caps"&gt;NBD&lt;/span&gt;&amp;nbsp;server.&lt;/p&gt;
&lt;div class="section" id="come-and-take-a-chance-with-me"&gt;
&lt;h2&gt;Come and take a chance with&amp;nbsp;me?&lt;/h2&gt;
&lt;p&gt;As mentioned in the last post, in trying to solve this I ventured through a
myriad of hacky or simple solutions, all attempting to work around the
&amp;#8220;caching&amp;#8221; of pages by the file-system driver on the server side. Yet again,
caching rears its head as one of the two &amp;#8220;hard problems&amp;#8221; of computer science
&lt;a class="footnote-reference" href="#hard" id="footnote-reference-1"&gt;[1]&lt;/a&gt;, though the root of the issue here is the assumption that the
file-system driver has exclusive access to the underlying block device (the
cache is merely a consequence of&amp;nbsp;this).&lt;/p&gt;
&lt;p&gt;In hindsight, I should&amp;#8217;ve looked at the pieces and concluded what I eventually
did: just write your own &lt;span class="caps"&gt;TFTP&lt;/span&gt; server! One which can read files directly out of
the boot partition of the &lt;span class="caps"&gt;OS&lt;/span&gt; image. This solution sounds like a sledgehammer to
crack a nut, but actually it&amp;#8217;s less complex than it seems. All it&amp;#8217;s got to
handle is simple partitioning (&lt;abbr title="Master Boot Record"&gt;&lt;span class="caps"&gt;MBR&lt;/span&gt;&lt;/abbr&gt; or &lt;abbr title="GUID Partition Tables"&gt;&lt;span class="caps"&gt;GPT&lt;/span&gt;&lt;/abbr&gt;), reading a &lt;abbr title="File Allocation Table"&gt;&lt;span class="caps"&gt;FAT&lt;/span&gt;&lt;/abbr&gt;
file-system , and &lt;abbr title="Trivial File Transfer Protocol"&gt;&lt;span class="caps"&gt;TFTP&lt;/span&gt;&lt;/abbr&gt;.&lt;/p&gt;
&lt;p&gt;I may not be selling this as &amp;#8220;simple&amp;#8221;, but let&amp;#8217;s break down what each of these
actually&amp;nbsp;entails:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;&lt;span class="caps"&gt;MBR&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Master_boot_record"&gt;Master Boot Record&lt;/a&gt; partitioning is an ancient method of partitioning a
&lt;span class="strike"&gt;floppy disk&lt;/span&gt; &lt;span class="strike"&gt;hard drive&lt;/span&gt; &lt;span class="strike"&gt;&lt;span class="caps"&gt;USB&lt;/span&gt; stick&lt;/span&gt; small
storage medium, but while it&amp;#8217;s very difficult to accurately &lt;em&gt;write&lt;/em&gt; (in
such a way that it&amp;#8217;ll actually … boot things), it&amp;#8217;s not hard to &lt;em&gt;read&lt;/em&gt;
(other than the minor mess that is logical partitioning, intended to get
around the original 4-partition limit). A few hours work.&amp;nbsp;Maybe.&lt;/dd&gt;
&lt;dt&gt;&lt;span class="caps"&gt;GPT&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/GUID_Partition_Table"&gt;&lt;span class="caps"&gt;GUID&lt;/span&gt; Partition Tables&lt;/a&gt; are the modern, and frankly trivial (by comparison
to &lt;span class="caps"&gt;MBR&lt;/span&gt;) means of partitioning storage media. No more than an hour needed&amp;nbsp;here.&lt;/dd&gt;
&lt;dt&gt;&lt;span class="caps"&gt;FAT&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/File_Allocation_Table"&gt;File Allocation Tables&lt;/a&gt; are another positively ancient piece of computing
history, used for storing files on just about every piece of computing
media with the possible exception of the tape drive (and I wouldn&amp;#8217;t put it
past someone to have tried that). The file-system isn&amp;#8217;t complicated in
basis (in fact I&amp;#8217;ve written a library for something very similar before
&lt;a class="footnote-reference" href="#beenthere" id="footnote-reference-2"&gt;[2]&lt;/a&gt;); the complexity comes from the myriad implementations that
use the same structures, but disagree subtly (and not so subtly) about the
meaning or even position of certain fields within&amp;nbsp;them.&lt;/p&gt;
&lt;p class="last"&gt;Still, a few evenings to get the basics done, then several more to work out
the rough edges (&lt;span class="caps"&gt;FAT&lt;/span&gt;-12, long filename handling, etc. etc.), then a few
more to build a nice Path-like &lt;span class="caps"&gt;API&lt;/span&gt; on top of it, and we&amp;#8217;re&amp;nbsp;good!&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;span class="caps"&gt;TFTP&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;a class="reference external" href="https://datatracker.ietf.org/doc/html/rfc1350"&gt;Trivial File Transfer Protocol&lt;/a&gt; is, tautologically, trivial. Getting the
basic protocol implemented took only a couple of hours (including testing
with several implementations). Then it was onto the extensions; I wound up
only bothering with those that the Pi bootloader attempts to negotiate and
one more that&amp;#8217;s useful for testing (&lt;tt class="docutils literal"&gt;tsize&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;blocksize&lt;/tt&gt;, and
&lt;tt class="docutils literal"&gt;timeout&lt;/tt&gt;) though hopefully I&amp;#8217;ve structured things suitably for a future
addition of the &lt;tt class="docutils literal"&gt;windowsize&lt;/tt&gt; option. Still, only a few more hours on&amp;nbsp;that.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;All in all, despite a few headaches (mostly around &lt;span class="caps"&gt;FAT&lt;/span&gt;&amp;#8217;s obscure history), it
wasn&amp;#8217;t difficult to throw all this together, stir it around a bit, and produce
what I needed (would&amp;#8217;ve been nice to make it into a &amp;#8220;learn Rust&amp;#8221; project, but I
was already &lt;em&gt;waaay&lt;/em&gt; overdue on this post series, so I wound up going with my
comfortable old&amp;nbsp;Python).&lt;/p&gt;
&lt;p&gt;The result is &lt;a class="reference external" href="https://github.com/waveform80/nobodd"&gt;nobodd&lt;/a&gt;: a &lt;span class="caps"&gt;TFTP&lt;/span&gt; boot-server that will read files directly out
of the &lt;span class="caps"&gt;FAT&lt;/span&gt; partition of an image without mounting&amp;nbsp;it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="nbd-nbd"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;NBD&lt;/span&gt;! &lt;span class="caps"&gt;NBD&lt;/span&gt;!&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s walk through a set up using &lt;tt class="docutils literal"&gt;nobodd&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-server&lt;/span&gt;&lt;/tt&gt;. The client
side is exactly the same as before so I&amp;#8217;ll just refer you to the start of the
&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/nbd-does-it-better.html"&gt;last post&lt;/a&gt; for that part. Go through
requirements (a Pi and a server), configuring the Pi for netboot, adding
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;linux-modules-extra-&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-client&lt;/span&gt;&lt;/tt&gt;, and transferring the regenerated
initramfs and identity to the server, and shutting down the Pi &lt;a class="footnote-reference" href="#headings" id="footnote-reference-3"&gt;[3]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, on the server we&amp;#8217;re going to do things a bit differently. This time we&amp;#8217;re
going to configure a &amp;#8220;template&amp;#8221; image that we can copy to &amp;#8220;instance&amp;#8221; images any
time we want a new install. And this time we&amp;#8217;re going to use the fancy little
&lt;a class="reference external" href="https://nobodd.readthedocs.io/en/stable/cli_prep.html"&gt;nobodd-prep&lt;/a&gt; tool from &lt;tt class="docutils literal"&gt;nobodd&lt;/tt&gt; to do&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Start as we did before by downloading the image and verifying&amp;nbsp;it:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Password:
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/srv/images&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/srv/images&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;http://cdimage.ubuntu.com/releases/22.04.3/release/ubuntu-22.04.3-preinstalled-server-arm64+raspi.img.xz&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt; ...
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;http://cdimage.ubuntu.com/releases/22.04.3/release/SHA256SUMS&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt; ...
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sha256sum&lt;span class="w"&gt; &lt;/span&gt;--check&lt;span class="w"&gt; &lt;/span&gt;--ignore-missing&lt;span class="w"&gt; &lt;/span&gt;SHA256SUMS&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;ubuntu-22.04.3-preinstalled-server-arm64+raspi.img.xz: OK
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;SHA256SUMS
&lt;/pre&gt;
&lt;p&gt;Now, we simply use &lt;a class="reference external" href="https://nobodd.readthedocs.io/en/stable/cli_prep.html"&gt;nobodd-prep&lt;/a&gt; to customize it. This tool can be used to
resize the image, re-write the &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; parameters, and copy files onto
the boot partition (including the customized &lt;tt class="docutils literal"&gt;initrd.img&lt;/tt&gt; and a cloud-init
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; file), all in one go. It can only operate on uncompressed images
though, so we still need to do&amp;nbsp;that:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;unxz&lt;span class="w"&gt; &lt;/span&gt;ubuntu-22.04.3-preinstalled-server-arm64+raspi.img.xz&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;ubuntu-22.04.3-preinstalled-server-arm64+raspi.img&lt;span class="w"&gt; &lt;/span&gt;jammy-template.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;nobodd-tools&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt; ...
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;user-data&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;package_update: true
packages:
- avahi-daemon
- nbd-client
- linux-modules-extra-raspi
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;jammy-template.img&lt;span class="w"&gt; &lt;/span&gt;jammy.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;nobodd-prep&lt;span class="w"&gt; &lt;/span&gt;--copy&lt;span class="w"&gt; &lt;/span&gt;user-data&lt;span class="w"&gt; &lt;/span&gt;--copy&lt;span class="w"&gt; &lt;/span&gt;initrd.img&lt;span class="w"&gt; &lt;/span&gt;--size&lt;span class="w"&gt; &lt;/span&gt;16GB&lt;span class="w"&gt; &lt;/span&gt;jammy.img
&lt;/pre&gt;
&lt;p&gt;And that&amp;#8217;s it! My hope is that, in noble (24.04) most of these steps will not
be necessary. In that release, it &lt;em&gt;should&lt;/em&gt; be possible to download the image
unpack it, give it a nice name, run &lt;a class="reference external" href="https://nobodd.readthedocs.io/en/stable/cli_prep.html"&gt;nobodd-prep&lt;/a&gt; to customize it, and go
from&amp;nbsp;there.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi, Future Dave here! Well, the &lt;abbr title="Main Inclusion Request"&gt;&lt;span class="caps"&gt;MIR&lt;/span&gt;&lt;/abbr&gt; for
&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/nbd/+bug/2054480"&gt;nbd-client&lt;/a&gt;, which I had hoped would be a simple affair, erm, wasn&amp;#8217;t
&lt;a class="footnote-reference" href="#easy" id="footnote-reference-4"&gt;[4]&lt;/a&gt;. It didn&amp;#8217;t make it into noble (24.04), or oracular (24.10). It
&lt;em&gt;might&lt;/em&gt; make it into plucky (25.04), thanks largely to the efforts of my
new side-kick &lt;a class="footnote-reference" href="#sidekick" id="footnote-reference-5"&gt;[5]&lt;/a&gt;. But this update is so overdue, I&amp;#8217;m just going
to post it anyway, and hope it&amp;#8217;ll be of use to Future You if/when the
relevant things finally make it into the&amp;nbsp;image.&lt;/p&gt;
&lt;p class="attribution"&gt;&amp;mdash;Future&amp;nbsp;Dave&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Next, we&amp;#8217;ll install and configure the required daemons. As before, we&amp;#8217;ll use
&lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt; as a &lt;span class="caps"&gt;DHCP&lt;/span&gt; proxy, and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-server&lt;/span&gt;&lt;/tt&gt;. However, this time instead of
&lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt; also handling &lt;span class="caps"&gt;TFTP&lt;/span&gt; duties, we&amp;#8217;ll use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nobodd-tftpd&lt;/span&gt;&lt;/tt&gt; for that.
First up, install the&amp;nbsp;packages:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;dnsmasq&lt;span class="w"&gt; &lt;/span&gt;nbd-server&lt;span class="w"&gt; &lt;/span&gt;nobodd-tftpd
&lt;/pre&gt;
&lt;p&gt;Next, configure &lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/dnsmasq.conf&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;interface=eth0
bind-interfaces
dhcp-range=192.168.255.255,proxy
pxe-service=0,&amp;quot;Raspberry Pi Boot&amp;quot;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;dnsmasq
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Adjust the reference to &lt;tt class="docutils literal"&gt;eth0&lt;/tt&gt; if your Ethernet &lt;span class="caps"&gt;NIC&lt;/span&gt; is named something
else. If your network&amp;#8217;s mask is not 192.168.255.255, adjust this&amp;nbsp;accordingly.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now the &lt;span class="caps"&gt;NBD&lt;/span&gt;&amp;nbsp;server:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;chown&lt;span class="w"&gt; &lt;/span&gt;nbd:nbd&lt;span class="w"&gt; &lt;/span&gt;jammy.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-lh&lt;span class="w"&gt; &lt;/span&gt;jammy.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;-rw-r--r-- 1 nbd  nbd 16.0G Dec  3 13:48 jammy.img
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/nbd-server/conf.d/jammy.conf&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;[jammy]
exportname = /srv/images/jammy.img
EOF
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;nbd-server
&lt;/pre&gt;
&lt;p&gt;And finally the &lt;span class="caps"&gt;TFTP&lt;/span&gt;&amp;nbsp;server:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;~ubuntu/pi-ident.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Serial          : 1000000089025d75
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nv"&gt;piserial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'1s/^Serial.*\([0-9a-f]\{8\}\)$/\1/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~ubuntu/pi-ident.txt&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$piserial&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;89025d75
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/nobodd/conf.d/jammy.conf&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;[board:$piserial]
image = /srv/images/jammy.img
partition = 1
EOF
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;reload&lt;span class="w"&gt; &lt;/span&gt;nobodd-tftpd
&lt;/pre&gt;
&lt;p&gt;The final config piece (&lt;tt class="docutils literal"&gt;/etc/nobodd/conf.d/jammy.conf&lt;/tt&gt;) ties the Pi&amp;#8217;s board
serial number to the image that we want to actually boot. The &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt;
on the boot partition of that image needs to point to the &lt;span class="caps"&gt;NBD&lt;/span&gt; server serving
that same image. This was actually handled implicitly by the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nobodd-prep&lt;/span&gt;&lt;/tt&gt;
command above. It&amp;nbsp;assumes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The &lt;span class="caps"&gt;NBD&lt;/span&gt; server is the machine it is running&amp;nbsp;on&lt;/li&gt;
&lt;li&gt;The &lt;span class="caps"&gt;NBD&lt;/span&gt; share will be named after the stem of the image&amp;#8217;s filename (in other
words it transforms &amp;#8220;jammy.img&amp;#8221; to&amp;nbsp;&amp;#8220;jammy&amp;#8221;)&lt;/li&gt;
&lt;li&gt;The root partition is the first non-&lt;span class="caps"&gt;FAT&lt;/span&gt;-type partition in the image
(partition 2 in this&amp;nbsp;case)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hence, in this case it will have automatically inserted the following in the
&lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; of the&amp;nbsp;image:&lt;/p&gt;
&lt;pre class="code text literal-block"&gt;
ip=dhcp nbdroot=ubuntu/jammy root=/dev/nbd0p2 ...
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="when-the-end-comes-i-know"&gt;
&lt;h2&gt;When the end comes, I&amp;nbsp;know&lt;/h2&gt;
&lt;p&gt;At this point, you should be ready to go. Power up the Pi, and if everything is
working correctly, it should netboot into your jammy image, applying the custom
cloud-init along the&amp;nbsp;way.&lt;/p&gt;
&lt;p&gt;How do you add more boards? Now you&amp;#8217;ve got your template image, the process
should be as simple&amp;nbsp;as:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Find your next board&amp;#8217;s serial&amp;nbsp;number&lt;/li&gt;
&lt;li&gt;Create another copy of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;jammy-template.img&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Customize it with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nobodd-prep&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Add the &lt;span class="caps"&gt;NBD&lt;/span&gt; server configuration, reload the &lt;span class="caps"&gt;NBD&lt;/span&gt;&amp;nbsp;server&lt;/li&gt;
&lt;li&gt;Add the &lt;span class="caps"&gt;TFTPD&lt;/span&gt; server configuration, reload the &lt;span class="caps"&gt;TFTPD&lt;/span&gt;&amp;nbsp;server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;#8217;s worth noting that &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nobodd-prep&lt;/span&gt;&lt;/tt&gt; is technically capable of generating the
required configurations for the last two items. However, I&amp;#8217;m a bit unhappy with
the design of it at the moment. It&amp;#8217;s become a horribly convoluted &amp;#8220;big&amp;#8221; tool,
and I think things would be much more flexible if it was &lt;a class="reference external" href="https://github.com/waveform80/nobodd/issues/9"&gt;broken up&lt;/a&gt; into a
series of much simpler tools that emulate the functionality of &lt;tt class="docutils literal"&gt;cat&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;cp&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;rm&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;ls&lt;/tt&gt;, and providing something that uses those pieces to achieve the
customization of the current&amp;nbsp;tool.&lt;/p&gt;
&lt;p&gt;Another aspect of this setup worth noting is that we haven&amp;#8217;t used &lt;tt class="docutils literal"&gt;mount&lt;/tt&gt;
anywhere. Neither &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-server&lt;/span&gt;&lt;/tt&gt; nor &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nobodd-tftpd&lt;/span&gt;&lt;/tt&gt; require mounts or loop
devices to operate. Why is this important? Because now this entire set up can
run in an unprivileged container, which makes testing things &lt;em&gt;much&lt;/em&gt; simpler.
There are still hoops to jump through &lt;a class="footnote-reference" href="#hoops" id="footnote-reference-6"&gt;[6]&lt;/a&gt; if you want to go this route, which are
beyond the scope of this post, but it is at least &lt;em&gt;possible&lt;/em&gt;.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="hard" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Alongside naming things and off-by-one errors …&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="beenthere" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I&amp;#8217;ve written &lt;a class="reference external" href="https://compound-files.readthedocs.io/"&gt;stuff&lt;/a&gt;
very similar to a &lt;span class="caps"&gt;FAT&lt;/span&gt; file system driver in Python before.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="headings" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In other words, follow the last post until &amp;#8220;Why&amp;#8217;d you have to be
so good?&amp;#8221;. Incidentally, I make no apologies for the musical puns in my
titles!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="easy" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;After several years in Foundations, you&amp;#8217;d think I&amp;#8217;d have learned by
now, wouldn&amp;#8217;t you? If it looks hard, it is. If it looks easy, it isn&amp;#8217;t.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="sidekick" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Yes! It&amp;#8217;s not just me anymore! Say &amp;#8220;hi&amp;#8221; to &lt;a class="reference external" href="https://launchpad.net/~r41k0u"&gt;r41k0u&lt;/a&gt; who&amp;#8217;s also
doing some sterling work on the camera stack this cycle.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="hoops" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Specifically this involves getting your container to appear
&lt;em&gt;directly&lt;/em&gt; on your network, rather than hiding behind &lt;span class="caps"&gt;NAT&lt;/span&gt;, so that it can
proxy &lt;span class="caps"&gt;DHCP&lt;/span&gt; requests&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="net"></category><category term="nbd"></category></entry><entry><title>NBD Does it Better</title><link href="https://waldorf.waveform.org.uk/2023/nbd-does-it-better.html" rel="alternate"></link><published>2023-10-30T00:00:00+00:00</published><updated>2024-12-03T15:52:56+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-10-30:/2023/nbd-does-it-better.html</id><summary type="html">&lt;p class="first last"&gt;An alternative means of net-booting a Pi, using &lt;span class="caps"&gt;NBD&lt;/span&gt; instead of &lt;span class="caps"&gt;NFS&lt;/span&gt; to
permit block-layer manipulation (docker overlays, snaps,&amp;nbsp;etc.)&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/nbd-knows.html"&gt;prior post&lt;/a&gt; we looked at the issues with
using &lt;abbr title="Network File System"&gt;&lt;span class="caps"&gt;NFS&lt;/span&gt;&lt;/abbr&gt; as our netboot root, and some of the
alternatives. This time we&amp;#8217;ll go through setting up the server and client of an
&lt;abbr title="Network Block Device"&gt;&lt;span class="caps"&gt;NBD&lt;/span&gt;&lt;/abbr&gt; netboot system. If you want to play along at
home you&amp;#8217;ll&amp;nbsp;need:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A Raspberry Pi. I&amp;#8217;ll be using a Pi 4B &lt;a class="footnote-reference" href="#earlypi" id="footnote-reference-1"&gt;[1]&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A real &lt;a class="footnote-reference" href="#novms" id="footnote-reference-2"&gt;[2]&lt;/a&gt; server running Ubuntu. I&amp;#8217;m going to be using another Pi for
this, but any old &lt;span class="caps"&gt;PC&lt;/span&gt; should be fine too (whatever server you use will benefit
from as much &lt;span class="caps"&gt;IO&lt;/span&gt; bandwidth, both disk and network, as you can&amp;nbsp;get).&lt;/li&gt;
&lt;li&gt;An ethernet network. You can&amp;#8217;t netboot a Pi over wifi, so you&amp;#8217;ll need an
ethernet set up for&amp;nbsp;this.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before we begin you should ensure your server is up and running, and that you
have remote &lt;span class="caps"&gt;SSH&lt;/span&gt; access to it. In the following instructions I&amp;#8217;m assuming that
your server&amp;#8217;s hostname is &lt;tt class="docutils literal"&gt;server&lt;/tt&gt;, and that you have an &lt;tt class="docutils literal"&gt;ubuntu&lt;/tt&gt; user on
it which can &lt;tt class="docutils literal"&gt;sudo&lt;/tt&gt; to root. Adjust according to your&amp;nbsp;setup!&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re going to start with the client side of things, oddly, but there is method
in the madness. Firstly, we need to ensure that the Pi&amp;#8217;s bootloader is
configured to attempt bootloading (which none are by default). And secondly, we
need to do some surgery on the initramfs for&amp;nbsp;later.&lt;/p&gt;
&lt;div class="section" id="some-kind-of-magic"&gt;
&lt;h2&gt;Some Kind of&amp;nbsp;Magic&lt;/h2&gt;
&lt;p&gt;We&amp;#8217;re going to be using the current Ubuntu &lt;abbr title="Long Term Service"&gt;&lt;span class="caps"&gt;LTS&lt;/span&gt;&lt;/abbr&gt;
(&amp;#8220;jammy&amp;#8221;, 22.04.3) throughout this guide, both on the server later, and for the
Pi&amp;#8217;s client image, to keep things relatively simple. Fire up &lt;a class="reference external" href="https://www.raspberrypi.com/software/"&gt;rpi-imager&lt;/a&gt; and
flash Ubuntu 22.04 server onto an &lt;span class="caps"&gt;SD&lt;/span&gt; card, then boot that &lt;span class="caps"&gt;SD&lt;/span&gt; card on your
chosen&amp;nbsp;Pi.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p class="last"&gt;Do &lt;em&gt;not&lt;/em&gt; be tempted to upgrade packages at this point. Specifically, the
kernel package must &lt;em&gt;not&lt;/em&gt; be upgraded&amp;nbsp;yet.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now we need to ensure that the Pi is configured to attempt network booting.
This is a one-time change which will be stored in the &lt;abbr title="Electrically Erasable Programmable Read-Only Memory"&gt;&lt;span class="caps"&gt;EEPROM&lt;/span&gt;&lt;/abbr&gt; of the Pi in question.
On the Pi, extract the current boot configuration from the &lt;span class="caps"&gt;EEPROM&lt;/span&gt;, modify the
existing &lt;tt class="docutils literal"&gt;BOOT_ORDER=&lt;/tt&gt; line (or append a new one if none is present), and
apply the modified&amp;nbsp;configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rpi-eeprom-config&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;boot.conf
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;boot.conf
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="go"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="go"&gt;BOOT_UART=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="go"&gt;WAKE_ON_GPIO=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="go"&gt;POWER_OFF_ON_HALT=0&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="hll"&gt;&lt;span class="go"&gt;BOOT_ORDER=0xf41&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/^BOOT_ORDER=/d&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;boot.conf
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BOOT_ORDER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0xf21&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;boot.conf
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;boot.conf
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="go"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="go"&gt;BOOT_UART=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="go"&gt;WAKE_ON_GPIO=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="go"&gt;POWER_OFF_ON_HALT=0&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="hll"&gt;&lt;span class="go"&gt;BOOT_ORDER=0xf21&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rpi-eeprom-config&lt;span class="w"&gt; &lt;/span&gt;--apply&lt;span class="w"&gt; &lt;/span&gt;boot.conf
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="go"&gt;Updating bootloader EEPROM&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="go"&gt; ...&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;reboot
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Obviously, feel free to fire up your favourite editor and just change the
&lt;tt class="docutils literal"&gt;BOOT_ORDER=&lt;/tt&gt; line yourself, instead of messing with &lt;tt class="docutils literal"&gt;sed&lt;/tt&gt;. The mysterious
&lt;tt class="docutils literal"&gt;0xf21&lt;/tt&gt; value is explained fully in the &lt;a class="reference external" href="https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#BOOT_ORDER"&gt;BOOT_ORDER documentation&lt;/a&gt; on the Raspberry Pi website, but simply means try the &lt;span class="caps"&gt;SD&lt;/span&gt; card
first (1), followed by the network (2), instead of &lt;span class="caps"&gt;USB&lt;/span&gt; boot (4) previously, and
if both fail then repeat (f) &lt;a class="footnote-reference" href="#usbalso" id="footnote-reference-3"&gt;[3]&lt;/a&gt;. The digits are specified in reverse
order for&amp;nbsp;$reasons.&lt;/p&gt;
&lt;p&gt;The reboot at the end is required to apply the new configuration to the boot
&lt;span class="caps"&gt;EEPROM&lt;/span&gt;. You can run &lt;tt class="docutils literal"&gt;sudo &lt;span class="pre"&gt;rpi-eeprom-config&lt;/span&gt;&lt;/tt&gt; after rebooting to check the
newly applied&amp;nbsp;configuration.&lt;/p&gt;
&lt;p&gt;Next, we need to install the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;linux-modules-extra-raspi&lt;/span&gt;&lt;/tt&gt; package for the
currently running kernel version. The reason is that the &lt;tt class="docutils literal"&gt;nbd&lt;/tt&gt; kernel module
was moved out of the default &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;linux-modules-raspi&lt;/span&gt;&lt;/tt&gt; package for efficiency. We
specifically need the version matching the running kernel version because
installing this package will regenerate the initramfs (&lt;tt class="docutils literal"&gt;initrd.img&lt;/tt&gt;). We&amp;#8217;ll
be copying that regenerated file into the image we&amp;#8217;re going to netboot and it
&lt;em&gt;must&lt;/em&gt; match the kernel version in that image. This is why it was important not
to upgrade any packages after the first&amp;nbsp;boot.&lt;/p&gt;
&lt;p&gt;We also need to install the &lt;span class="caps"&gt;NBD&lt;/span&gt; client package. This will add the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-client&lt;/span&gt;&lt;/tt&gt; executable to the initramfs, along with some scripts to call it
if the kernel command line specifies an &lt;span class="caps"&gt;NBD&lt;/span&gt; device as root &lt;a class="footnote-reference" href="#mantic" id="footnote-reference-4"&gt;[4]&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;linux-modules-extra-&lt;span class="k"&gt;$(&lt;/span&gt;uname&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nbd-client
&lt;/pre&gt;
&lt;p&gt;We need to gather one piece of information about the client Pi for use later on
the server: its serial number. We&amp;#8217;ll store this in a file and copy it and the
&lt;tt class="docutils literal"&gt;initrd.img&lt;/tt&gt; to the server. Finally, we&amp;#8217;ll shut down the Pi and move to the
server side of&amp;nbsp;things:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;Serial&lt;span class="w"&gt; &lt;/span&gt;/proc/cpuinfo&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;pi-ident.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;pi-ident.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Serial          : 1000000089025d75
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;scp&lt;span class="w"&gt; &lt;/span&gt;-q&lt;span class="w"&gt; &lt;/span&gt;pi-ident.txt&lt;span class="w"&gt; &lt;/span&gt;ubuntu&amp;#64;server:&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;scp&lt;span class="w"&gt; &lt;/span&gt;-q&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/initrd.img&lt;span class="w"&gt; &lt;/span&gt;ubuntu&amp;#64;server:&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;poweroff
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="why-d-you-have-to-be-so-good"&gt;
&lt;h2&gt;Why&amp;#8217;d you have to be so&amp;nbsp;good?&lt;/h2&gt;
&lt;p&gt;The first thing to do on the server is get the image &lt;a class="footnote-reference" href="#imgcache" id="footnote-reference-5"&gt;[5]&lt;/a&gt; we want to
serve, and do a little surgery on it. We flashed Ubuntu 22.04.3 so we set up a
directory under &lt;tt class="docutils literal"&gt;/srv&lt;/tt&gt; to hold the image, &lt;tt class="docutils literal"&gt;wget&lt;/tt&gt; it, and check the
&lt;abbr title="Secure Hash Algorithm 256-bit"&gt;&lt;span class="caps"&gt;SHA256&lt;/span&gt;&lt;/abbr&gt; checksum. Note that we&amp;#8217;re going
to perform most of these steps as&amp;nbsp;root:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Password:
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/srv/images&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/srv/images&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;http://cdimage.ubuntu.com/releases/22.04.3/release/ubuntu-22.04.3-preinstalled-server-arm64+raspi.img.xz&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt; ...
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;http://cdimage.ubuntu.com/releases/22.04.3/release/SHA256SUMS&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt; ...
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sha256sum&lt;span class="w"&gt; &lt;/span&gt;--check&lt;span class="w"&gt; &lt;/span&gt;--ignore-missing&lt;span class="w"&gt; &lt;/span&gt;SHA256SUMS&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;ubuntu-22.04.3-preinstalled-server-arm64+raspi.img.xz: OK
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;SHA256SUMS
&lt;/pre&gt;
&lt;p&gt;Now we&amp;#8217;re going to unpack the image (it&amp;#8217;s no good mounting something that&amp;#8217;s &lt;span class="caps"&gt;XZ&lt;/span&gt;
compressed), rename it to something more manageable , and expand the image file
to the full size of &lt;span class="caps"&gt;SD&lt;/span&gt; card we want to emulate (I&amp;#8217;m using &lt;span class="caps"&gt;8GB&lt;/span&gt; here, but change
the &lt;tt class="docutils literal"&gt;fallocate&lt;/tt&gt; command&amp;nbsp;accordingly):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;unxz&lt;span class="w"&gt; &lt;/span&gt;ubuntu-22.04.3-preinstalled-server-arm64+raspi.img.xz&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;ubuntu-22.04.3-preinstalled-server-arm64+raspi.img&lt;span class="w"&gt; &lt;/span&gt;jammy.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-lh&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;-rw-rw-r-- 1 root root 4.0G Oct  5 14:12 jammy.img
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;fallocate&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;8G&lt;span class="w"&gt; &lt;/span&gt;jammy.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-lh&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;-rw-rw-r-- 1 root root 8.0G Oct  5 14:51 jammy.img&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;ve expanded the image, but naturally the partitions inside it haven&amp;#8217;t been
changed in size, and nor have the file-systems inside those partitions.
However, that&amp;#8217;s fine. This is exactly what a freshly flashed &lt;span class="caps"&gt;8GB&lt;/span&gt; &lt;span class="caps"&gt;SD&lt;/span&gt; card looks
like. The device (in this case the image file) is &lt;span class="caps"&gt;8GB&lt;/span&gt;, but the root partition
inside is a mere 3.7 &lt;span class="caps"&gt;GB&lt;/span&gt;. We can use &lt;tt class="docutils literal"&gt;fdisk&lt;/tt&gt; to see&amp;nbsp;this:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;fdisk&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;jammy.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Disk jammy.img: 8 GiB, 8589934592 bytes, 16777216 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x542d34fa

Device     Boot  Start     End Sectors  Size Id Type
jammy.img1 *      2048  526335  524288  256M  c W95 FAT32 (LBA)
jammy.img2      526336 8320243 7793908  3.7G 83 Linux&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;On first boot of a &lt;span class="caps"&gt;8GB&lt;/span&gt; &lt;span class="caps"&gt;SD&lt;/span&gt; card, the cloud-init service checks its
configuration, sees that it should expand the root, re-writes the root
partition, and expands the file-system. Exactly the same will happen here when
we first boot this&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;Next, we need to overwrite the &lt;tt class="docutils literal"&gt;initrd.img&lt;/tt&gt; in the boot partition of this
image, with the one we generated on our client (and which we copied to the
server earlier). In order to do so we need to mount this image. Normally, if
the image file contained &lt;em&gt;only&lt;/em&gt; the file-system we wanted to manipulate, this
would be as trivial as running &lt;tt class="docutils literal"&gt;mount &lt;span class="pre"&gt;-o&lt;/span&gt; loop jammy.img &lt;span class="pre"&gt;some-path&lt;/span&gt;&lt;/tt&gt;. But this
image contains &lt;em&gt;partitions&lt;/em&gt;, so the file-system we wish to mount isn&amp;#8217;t at the
start of the&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;To get around this, instead of having a loop device created implicitly (with
mount&amp;#8217;s &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-o&lt;/span&gt; loop&lt;/tt&gt; option), we need to make our own loop-device and tell the
kernel to scan it for partitions. Then we&amp;#8217;ll create a mount-point and mount the
first partition there. Finally, we&amp;#8217;ll copy our customized &lt;tt class="docutils literal"&gt;initrd.img&lt;/tt&gt; into
the&amp;nbsp;mount-point:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;--find&lt;span class="w"&gt; &lt;/span&gt;--show&lt;span class="w"&gt; &lt;/span&gt;--partscan&lt;span class="w"&gt; &lt;/span&gt;jammy.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;/dev/loop5
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;/dev/loop5*&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;brw-rw---- 1 root disk   7, 5 Oct  5 21:20 /dev/loop5
brw-rw---- 1 root disk 259, 0 Oct  5 21:20 /dev/loop5p1
brw-rw---- 1 root disk 259, 1 Oct  5 21:20 /dev/loop5p2
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;boot/jammy&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/loop5p1&lt;span class="w"&gt; &lt;/span&gt;/srv/images/boot/jammy&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;initrd.img&lt;span class="w"&gt; &lt;/span&gt;boot/jammy/
&lt;/pre&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p class="last"&gt;The loop device on your system will likely have a different number; adjust
&lt;tt class="docutils literal"&gt;/dev/loop5&lt;/tt&gt; references&amp;nbsp;accordingly.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Next, we should also customize the cloud-init initial configuration to ensure
the image installs the same packages that we installed on the client&amp;nbsp;earlier:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;boot/jammy/user-data&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;package_update: true
packages:
- avahi-daemon
- nbd-client
- linux-modules-extra-raspi
EOF&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;If we don&amp;#8217;t do this, the next time our netbooted client refreshes its
initramfs, it would generate it &lt;em&gt;without&lt;/em&gt; the &lt;span class="caps"&gt;NBD&lt;/span&gt; client (and would naturally
fail at the next&amp;nbsp;reboot).&lt;/p&gt;
&lt;p&gt;Now we need to edit the kernel command line to tell it that its root device is
an &lt;span class="caps"&gt;NBD&lt;/span&gt; share. The kernel command line is one long line of text with
space-separated portions. We&amp;#8217;re going to change those space-separated bits into
individual lines to make it easier to manipulate, remove the existing
&lt;tt class="docutils literal"&gt;root=&lt;span class="caps"&gt;LABEL&lt;/span&gt;=writable&lt;/tt&gt; portion &lt;a class="footnote-reference" href="#labelroot" id="footnote-reference-6"&gt;[6]&lt;/a&gt;, and insert the following
portions&amp;nbsp;instead:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;ip=dhcp&lt;/tt&gt; &amp;#8212; we need an &lt;span class="caps"&gt;IP&lt;/span&gt; address to find the root device, and that it
should obtain it via &lt;span class="caps"&gt;DHCP&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;nbdroot=server/jammy&lt;/tt&gt; &amp;#8212; set up an &lt;span class="caps"&gt;NBD&lt;/span&gt; client and connect to the &lt;tt class="docutils literal"&gt;jammy&lt;/tt&gt;
share on the host &lt;tt class="docutils literal"&gt;server&lt;/tt&gt; (adjust this to match your server&amp;#8217;s name or &lt;span class="caps"&gt;IP&lt;/span&gt;&amp;nbsp;address)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;root=/dev/nbd0p2&lt;/span&gt;&lt;/tt&gt; &amp;#8212; find the actual root on the second partition of the
connected &lt;span class="caps"&gt;NBD&lt;/span&gt;&amp;nbsp;device&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of doing my usual confusing one-liner, we&amp;#8217;ll step through the actions
below, but feel free to fire up your favourite text editor and just edit
&lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; directly if you find that&amp;nbsp;easier:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;boot/jammy/cmdline.txt
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="go"&gt;console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;boot/jammy/cmdline.txt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/cmdline.txt
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/tmp/cmdline.txt
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="go"&gt;console=serial0,115200&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="go"&gt;dwc_otg.lpm_enable=0&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="go"&gt;console=tty1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="hll"&gt;&lt;span class="go"&gt;root=LABEL=writable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="go"&gt;rootfstype=ext4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="go"&gt;rootwait&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="go"&gt;fixrtc&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="go"&gt;quiet&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="go"&gt;splash&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/^root=/ s@=.*$@=/dev/nbd0p2@&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/cmdline.txt
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/^root=/ i ip=dhcp&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/cmdline.txt
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/^root=/ i nbdroot=server/jammy&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/cmdline.txt
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/tmp/cmdline.txt
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="go"&gt;console=serial0,115200&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="go"&gt;dwc_otg.lpm_enable=0&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="go"&gt;console=tty1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="hll"&gt;&lt;span class="go"&gt;ip=dhcp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="hll"&gt;&lt;span class="go"&gt;nbdroot=server/jammy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="hll"&gt;&lt;span class="go"&gt;root=/dev/nbd0p2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="go"&gt;rootfstype=ext4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="go"&gt;rootwait&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="go"&gt;fixrtc&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="go"&gt;quiet&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="go"&gt;splash&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;paste&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/cmdline.txt&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;boot/jammy/cmdline.txt
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;boot/jammy/cmdline.txt
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="go"&gt;console=serial0,115200 dwc_otg.lpm_enable=0 console=tty1 ip=dhcp nbdroot=server/jammy root=/dev/nbd0p2 rootfstype=ext4 rootwait fixrtc quiet splash&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now it&amp;#8217;s time to configure the &lt;abbr title="Dynamic Host Configuration Protocol"&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt;&lt;/abbr&gt; proxy and &lt;span class="caps"&gt;NBD&lt;/span&gt; server we talked about in the last article. We&amp;#8217;ll
start with the packages we&amp;#8217;re going to need: the &lt;span class="caps"&gt;NBD&lt;/span&gt; server itself, and the
ubiquitous &lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt; daemon which will be handling &lt;span class="caps"&gt;DHCP&lt;/span&gt;, and &lt;abbr title="Trivial File Transfer Protocol"&gt;&lt;span class="caps"&gt;TFTP&lt;/span&gt;&lt;/abbr&gt; for our netbooting&amp;nbsp;clients.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Don&amp;#8217;t worry if you&amp;#8217;ve already got a &lt;span class="caps"&gt;DHCP&lt;/span&gt; server on your network. I&amp;#8217;ve
assumed that you almost certainly do and will be configuring the &lt;span class="caps"&gt;DHCP&lt;/span&gt;
portion of &lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt; in &lt;span class="caps"&gt;DHCP&lt;/span&gt; &amp;#8220;proxy&amp;#8221; mode where it simply steps in to
augment the options transmitted by the authoritative &lt;span class="caps"&gt;DHCP&lt;/span&gt;&amp;nbsp;server.&lt;/p&gt;
&lt;p class="last"&gt;To put it another way: you shouldn&amp;#8217;t have to dismantle or reconfigure your
network to play&amp;nbsp;along!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Install the required&amp;nbsp;packages:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;dnsmasq&lt;span class="w"&gt; &lt;/span&gt;nbd-server
&lt;/pre&gt;
&lt;p&gt;During this installation you may see several warnings about &lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt; being
unable to start due to the address already being in use. This is normal and
occurs because &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;systemd-resolved&lt;/span&gt;&lt;/tt&gt; is already listening on port 53 (the &lt;span class="caps"&gt;DNS&lt;/span&gt;
port) for the loopback address, so it can cache &lt;span class="caps"&gt;DNS&lt;/span&gt; requests. We now configure
&lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt; to only listen on port 53 of the &lt;em&gt;ethernet&lt;/em&gt; &lt;abbr title="Network Interface Card"&gt;&lt;span class="caps"&gt;NIC&lt;/span&gt;&lt;/abbr&gt;, to act as a &lt;span class="caps"&gt;DHCP&lt;/span&gt; proxy, and &lt;span class="caps"&gt;TFTP&lt;/span&gt;&amp;nbsp;server:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/dnsmasq.conf&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;interface=eth0
bind-interfaces
dhcp-range=192.168.255.255,proxy
pxe-service=0,&amp;quot;Raspberry Pi Boot&amp;quot;
enable-tftp
tftp-root=/srv/images/boot
EOF
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;dnsmasq
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Adjust the reference to &lt;tt class="docutils literal"&gt;eth0&lt;/tt&gt; if your Ethernet &lt;span class="caps"&gt;NIC&lt;/span&gt; is named something
else. If your network&amp;#8217;s mask is not 192.168.255.255, adjust this&amp;nbsp;accordingly.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Next up is the &lt;span class="caps"&gt;NBD&lt;/span&gt; server, which simply needs to point the share &amp;#8220;jammy&amp;#8221; at our
&amp;#8220;jammy.img&amp;#8221;. However, we also need to remember to change ownership of our
images so the unprivileged &amp;#8220;nbd&amp;#8221; user can write to&amp;nbsp;it:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;chown&lt;span class="w"&gt; &lt;/span&gt;nbd:nbd&lt;span class="w"&gt; &lt;/span&gt;jammy.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-lh&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;total 8.1G
drwxr-xr-x 3 root root 4.0K Oct 30 16:49 boot
-rw-r--r-- 1 nbd  nbd  8.0G Oct 30 16:55 jammy.img
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/nbd-server/conf.d/jammy.conf&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;[jammy]
exportname = /srv/images/jammy.img
EOF
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;nbd-server
&lt;/pre&gt;
&lt;p&gt;Finally, we link our Pi&amp;#8217;s serial number (or more precisely, the last 8 digits
of it, if it&amp;#8217;s longer than that) with the mounted boot&amp;nbsp;partition.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;~ubuntu/pi-ident.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Serial          : 1000000089025d75
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nv"&gt;piserial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'1s/^Serial.*\([0-9a-f]\{8\}\)$/\1/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~ubuntu/pi-ident.txt&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$piserial&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;89025d75
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;ln&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;jammy&lt;span class="w"&gt; &lt;/span&gt;boot/&lt;span class="nv"&gt;$piserial&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;total 3
lrwxrwxrwx 1 root root    5 Oct 30 16:49 89025d75 -&amp;gt; jammy
drwxr-xr-x 3 root root 2560 Jan  1  1970 jammy&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="keeps-me-from-runnin"&gt;
&lt;h2&gt;Keeps Me From&amp;nbsp;Runnin&amp;#8217;&lt;/h2&gt;
&lt;p&gt;You&amp;#8217;re now at a point where you can try netbooting your client Pi. Remove its
&lt;span class="caps"&gt;SD&lt;/span&gt; card, and plug it in. You should see the &amp;#8220;rainbow&amp;#8221; boot screen appear fairly
quickly, but there&amp;#8217;ll be a &lt;em&gt;long&lt;/em&gt; pause on that screen. The reason is that your
Pi is transferring &lt;tt class="docutils literal"&gt;initrd.img&lt;/tt&gt; (which is now much larger than normal due to
our installation of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;linux-modules-extra&lt;/span&gt;&lt;/tt&gt;) over &lt;span class="caps"&gt;TFTP&lt;/span&gt; which is not an
efficient protocol without certain extensions, which the Pi&amp;#8217;s bootloader
doesn&amp;#8217;t implement. However, eventually you should be greeted by the typical
Linux kernel log scrolling by and reach a typical &amp;#8220;booted&amp;#8221; state the same as
you would with an &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card.&lt;/p&gt;
&lt;p&gt;If you hit any snags here, the following things are worth&amp;nbsp;checking:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Pay attention to any errors shown on the Pi&amp;#8217;s bootloader screen. In
particular, you should be able to see the Pi obtaining an &lt;span class="caps"&gt;IP&lt;/span&gt; address via &lt;span class="caps"&gt;DHCP&lt;/span&gt;
and various &lt;span class="caps"&gt;TFTP&lt;/span&gt; request&amp;nbsp;attempts.&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;journalctl &lt;span class="pre"&gt;-f&lt;/span&gt; &lt;span class="pre"&gt;--unit&lt;/span&gt; dnsmasq.service&lt;/tt&gt; on your server to follow the
dnsmasq log output. Again, if things are working, you should be seeing
several &lt;span class="caps"&gt;TFTP&lt;/span&gt; requests here. If you see nothing, double check the network mask
is specified correctly in the dnsmasq configuration, and that any firewall on
the server is permitting inbound traffic to port 69 (the &lt;span class="caps"&gt;TFTP&lt;/span&gt;&amp;nbsp;port).&lt;/li&gt;
&lt;li&gt;You &lt;em&gt;will&lt;/em&gt; see numerous &amp;#8220;Early terminate&amp;#8221; &lt;span class="caps"&gt;TFTP&lt;/span&gt; errors in the dnsmasq log
output. This is normal, and appears to be how the Pi&amp;#8217;s bootloader operates
(my guess would be it&amp;#8217;s attempting to determine the size of a file with the
tsize extension, terminating the transfer, allocating &lt;span class="caps"&gt;RAM&lt;/span&gt; for the file, then
starting the transfer&amp;nbsp;again).&lt;/li&gt;
&lt;li&gt;If cloud-init&amp;#8217;s final phase running &lt;tt class="docutils literal"&gt;apt update&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;apt install
&lt;span class="pre"&gt;avahi-daemon&lt;/span&gt; &lt;span class="pre"&gt;linux-modules-extra-raspi&lt;/span&gt; &lt;span class="pre"&gt;nbd-client&lt;/span&gt;&lt;/tt&gt; fails (it seems to
randomly on my test Pi), just login and run them&amp;nbsp;manually.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point you should have a fully booted system with a block device as the
root. All is good! We can use anything we would on a regular Pi with an &lt;span class="caps"&gt;SD&lt;/span&gt;
card, including snapd, docker, or anything else. But there&amp;#8217;s a rather serious
problem waiting silently for us. If things have worked correctly,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-client&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;linux-modules-extra-raspi&lt;/span&gt;&lt;/tt&gt; will have been installed. This
will have re-built the initramfs, and likely also have upgraded the kernel
package. If you attempt to reboot at this point, you&amp;#8217;ll likely find the next
boot fails on the rainbow&amp;nbsp;screen.&lt;/p&gt;
&lt;p&gt;Spot the problem? &lt;em&gt;Two&lt;/em&gt; things are accessing the boot partition&amp;#8217;s block device.
First, the &lt;span class="caps"&gt;TFTP&lt;/span&gt; server (&lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt;) is reading the boot partition via a loop
device. Second, the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-server&lt;/span&gt;&lt;/tt&gt; is serving the boot partition directly from
the&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;Recall that mounts of block devices assume they have &lt;em&gt;exclusive access&lt;/em&gt; to the
underlying block device. But here, the &lt;tt class="docutils literal"&gt;vfat&lt;/tt&gt; driver on the server does &lt;em&gt;not&lt;/em&gt;
have exclusive access to the boot partition. What happens if we change the boot
partition on the client. Does the server&amp;nbsp;notice?&lt;/p&gt;
&lt;p&gt;Try the following&amp;nbsp;experiment:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;On the client: &lt;tt class="docutils literal"&gt;sudo touch /boot/firmware/foo&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;On the server: &lt;tt class="docutils literal"&gt;ls /srv/images/boot/jammy/foo&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should find that, even if you &lt;tt class="docutils literal"&gt;sudo sync&lt;/tt&gt; on the client the &lt;tt class="docutils literal"&gt;foo&lt;/tt&gt; file
simply won&amp;#8217;t show up on the server&amp;#8217;s boot mount. The boot mount needs
&lt;em&gt;remounting&lt;/em&gt; to make it re-read the necessary blocks from the underlying image.
This bodes badly for anything that writes to the boot partition, as happens
when installing a new kernel, or anything that causes the initramfs to be&amp;nbsp;rebuilt.&lt;/p&gt;
&lt;p&gt;Thus, to resurrect your netbooting Pi do the following on the&amp;nbsp;server:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/srv/images/boot/jammy&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/loop5p1&lt;span class="w"&gt; &lt;/span&gt;/srv/images/boot/jammy
&lt;/pre&gt;
&lt;p&gt;Well, this sucks.&amp;nbsp;Solutions?&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Use &lt;span class="caps"&gt;NFS&lt;/span&gt; for the boot partition mount instead of &lt;span class="caps"&gt;NBD&lt;/span&gt;. This will work, but
there&amp;#8217;re several issues here. Firstly … it requires an &lt;span class="caps"&gt;NFS&lt;/span&gt; server! Secondly,
you need to customize the &lt;tt class="docutils literal"&gt;fstab&lt;/tt&gt; in the image before first boot (urgh).
Thirdly you need to extract the content of the boot partition to an exported
directory and point &lt;span class="caps"&gt;TFTP&lt;/span&gt; to that (which wastes space) &lt;em&gt;or&lt;/em&gt; you can stick
with the looped mount, but you must be absolutely certain that you mount the
boot partition with &lt;span class="caps"&gt;NFS&lt;/span&gt; on the client, &lt;em&gt;not&lt;/em&gt; &lt;span class="caps"&gt;NBD&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Try and remount the boot partition when the client reboots. This is risky as
it&amp;#8217;s almost impossible to guarantee in practice. If you think you can get
clever with &lt;span class="caps"&gt;DHCP&lt;/span&gt; hook scripts in &lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt; to remount just as the machine
is booting (because &lt;span class="caps"&gt;DHCP&lt;/span&gt; comes before the first &lt;span class="caps"&gt;TFTP&lt;/span&gt; request), you&amp;#8217;re wrong
&lt;a class="footnote-reference" href="#askmehow" id="footnote-reference-7"&gt;[7]&lt;/a&gt;. &lt;tt class="docutils literal"&gt;dnsmasq&lt;/tt&gt; scripts run asynchronously and you can&amp;#8217;t unmount a
&amp;#8220;busy&amp;#8221; partition. It&amp;#8217;s still possible to do this manually, but that&amp;#8217;s
boring! &lt;a class="footnote-reference" href="#noremount" id="footnote-reference-8"&gt;[8]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Write our own &lt;span class="caps"&gt;TFTP&lt;/span&gt; server that directly accesses the image without ever
mounting it!&amp;nbsp;Hmmm…&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Join me next time for the conclusion of this series where … well, you can
probably guess where this is going&amp;nbsp;…&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="earlypi" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The Pi 2B, 3B, and 3B+ &lt;em&gt;can&lt;/em&gt; also netboot, but the instructions
differ on each, and this article is long enough as it is. See &lt;a class="reference external" href="https://www.raspberrypi.com/documentation/computers/remote-access.html#network-boot-your-raspberry-pi"&gt;Network
boot&lt;/a&gt; for more information, and drop me a question in the comments if you
need more!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="novms" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Your server needs to be a &amp;#8220;real&amp;#8221; server, or at the very least a
full virtual machine, not a container. Specifically, you need to be able to
create loop devices and mount images (both of which are not typically
possible in a container environment).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="usbalso" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;If you just want to &lt;em&gt;add&lt;/em&gt; network boot instead of replacing the
&lt;span class="caps"&gt;USB&lt;/span&gt; boot option, that&amp;#8217;s fine too &amp;#8212; you can use &lt;tt class="docutils literal"&gt;0xf421&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;0xf241&lt;/tt&gt;
here too. Also, the reason we&amp;#8217;ve left &lt;span class="caps"&gt;SD&lt;/span&gt; card boot in the order is simply
for safety (there are recovery methods available even if you remove it but
it&amp;#8217;s simpler to just leave it there).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="mantic" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In mantic, the &lt;tt class="docutils literal"&gt;nbd&lt;/tt&gt; module moved back to linux-modules-raspi,
so you can skip installing &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;linux-modules-extra&lt;/span&gt;&lt;/tt&gt; there if you want, but
you&amp;#8217;ll still need &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-client&lt;/span&gt;&lt;/tt&gt;. In noble, I&amp;#8217;m intending to seed
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbd-client&lt;/span&gt;&lt;/tt&gt; in the image, so no modifications at all should be required.
It&amp;#8217;s a pretty small install (&lt;span class="caps"&gt;172KB&lt;/span&gt;) so it constitutes little bloat, and the
ability to netboot out of the box (with an appropriately configured Pi)
seems to justify the change to me.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="imgcache" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;If you&amp;#8217;re cunning, you can avoid downloading the image again by
extracting it from the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;rpi-imager&lt;/span&gt;&lt;/tt&gt; cache. On Linux at least, this is
under &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.cache/Raspberry&lt;/span&gt; Pi/Imager/lastdownload.cache&lt;/tt&gt;. Because the
filename has lost it&amp;#8217;s extension I usually use &lt;tt class="docutils literal"&gt;file&lt;/tt&gt; to determine what it
was (e.g. if it&amp;#8217;s &amp;#8220;&lt;span class="caps"&gt;XZ&lt;/span&gt; compressed data, checksum &lt;span class="caps"&gt;CRC64&lt;/span&gt;&amp;#8221; I know it was
&lt;tt class="docutils literal"&gt;.img.xz&lt;/tt&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="labelroot" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Unfortunately, the initramfs isn&amp;#8217;t intelligent enough to go
searching for a label-based root in an &lt;span class="caps"&gt;NBD&lt;/span&gt; device, even though technically
this is valid.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="askmehow" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-7"&gt;[7]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Ask me how I know this!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="noremount" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-8"&gt;[8]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Before anyone asks, no &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-o&lt;/span&gt; remount&lt;/tt&gt; isn&amp;#8217;t sufficient. It
needs to be completely unmounted and re-mounted.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="net"></category><category term="nbd"></category></entry><entry><title>NBD Knows</title><link href="https://waldorf.waveform.org.uk/2023/nbd-knows.html" rel="alternate"></link><published>2023-10-27T00:00:00+01:00</published><updated>2024-12-03T15:52:56+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-10-27:/2023/nbd-knows.html</id><summary type="html">&lt;p class="first last"&gt;The problems with &lt;span class="caps"&gt;NFS&lt;/span&gt; as a netboot system, and what the alternatives&amp;nbsp;are&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This is part 1 of an exploration of netbooting Raspberry Pis, with an emphasis
on &lt;span class="caps"&gt;NBD&lt;/span&gt; over the more traditional &lt;span class="caps"&gt;NFS&lt;/span&gt;. While obviously we&amp;#8217;re going to be looking
at Ubuntu in this series, I&amp;#8217;m hoping this should be generic enough to be useful
to be useful on a variety of&amp;nbsp;distributions.&lt;/p&gt;
&lt;div class="section" id="the-trouble-i-ve-seen"&gt;
&lt;h2&gt;The trouble I&amp;#8217;ve&amp;nbsp;seen&lt;/h2&gt;
&lt;p&gt;The obvious question is &amp;#8220;why &lt;abbr title="Network Block Device"&gt;&lt;span class="caps"&gt;NBD&lt;/span&gt;&lt;/abbr&gt;?&amp;#8221; or put
another way &amp;#8220;what&amp;#8217;s wrong with &lt;abbr title="Network File Sytem"&gt;&lt;span class="caps"&gt;NFS&lt;/span&gt;&lt;/abbr&gt;?&amp;#8221;. After all,
&lt;span class="caps"&gt;NFS&lt;/span&gt; is the current &amp;#8220;best practice&amp;#8221; for booting Raspberry Pis, and widely used,
including by some very large installations (e.g. &lt;a class="reference external" href="https://mythic-beasts.com/"&gt;Mythic Beasts&amp;#8217;&lt;/a&gt; fantastic Pi&amp;nbsp;cloud).&lt;/p&gt;
&lt;p&gt;Ultimately, the reason is that the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Network_File_System"&gt;Network File System&lt;/a&gt; … is a&amp;nbsp;file-system.&lt;/p&gt;
&lt;p&gt;What a ridiculous objection, of course it&amp;#8217;s a file-system! The clue&amp;#8217;s in the
name! And surely you &lt;em&gt;want&lt;/em&gt; a file-system? More typically, though, you actually
have a root &lt;em&gt;block device&lt;/em&gt;, which your kernel will then &lt;em&gt;transform&lt;/em&gt; into a
file-system, rather than being given a file-system&amp;nbsp;directly.&lt;/p&gt;
&lt;p&gt;What problems arise from the lack of (access to) an underlying block device?
The crux of the issue is what the kernel can assume about the file-system, most
particularly whether it can assume it has exclusive access to it, and whether
it can trivially access the underlying blocks of certain&amp;nbsp;files.&lt;/p&gt;
&lt;p&gt;Consider the common &amp;#8220;root is a block device&amp;#8221;&amp;nbsp;case:&lt;/p&gt;
&lt;p&gt;The kernel on &lt;em&gt;your&lt;/em&gt; machine runs the transformation (the file system driver)
that converts that block device into a file-system. Crucially, it can assume
that it is the &lt;em&gt;only&lt;/em&gt; entity accessing this block device (that it has exclusive
access), and that for certain operations it can (with some jiggery-pokery)
bypass the file-system and treat a file as a block device &lt;a class="footnote-reference" href="#simples" id="footnote-reference-1"&gt;[1]&lt;/a&gt;, which
makes certain things nice and&amp;nbsp;simple.&lt;/p&gt;
&lt;p&gt;Need to allocate a large contiguous file, for example as a swap file? No
problem! The file-system driver implements this &lt;a class="footnote-reference" href="#btrfs" id="footnote-reference-2"&gt;[2]&lt;/a&gt;, and can even give
the kernel the contiguous blocks for use in the swap system (remember how block
devices &amp;#8220;make storage look like &lt;span class="caps"&gt;RAM&lt;/span&gt;&amp;#8221;?&amp;nbsp;Foreshadowing!).&lt;/p&gt;
&lt;p&gt;Need to lock a file? The kernel knows nothing else is mounting file systems
from that block device, so it knows all the locks that exist on it and doesn&amp;#8217;t
need to coordinate with anything else. Likewise, caching is simple
&lt;a class="footnote-reference" href="#neversimple" id="footnote-reference-3"&gt;[3]&lt;/a&gt;. The kernel can assume it has absolute knowledge of which
blocks are dirty and need writing back because nothing else can be producing
file systems from that block&amp;nbsp;device.&lt;/p&gt;
&lt;p&gt;Need some temporary file space, private to your process? Create a unique
temporary file and delete it, leaving the file handle open, then use the file
as a temporary store. The space won&amp;#8217;t be reclaimed because, even though there
are no links to the file, the kernel knows there&amp;#8217;s still an open file&amp;nbsp;handle.&lt;/p&gt;
&lt;p&gt;Now the &amp;#8220;root is a file-system&amp;#8221;&amp;nbsp;case:&lt;/p&gt;
&lt;p&gt;In this case, there&amp;#8217;s a block device &lt;em&gt;somewhere&lt;/em&gt;, but your kernel has no
visibility of it, and must assume that other entities can change its blocks
without&amp;nbsp;notification.&lt;/p&gt;
&lt;p&gt;This interferes with several of the scenarios above. Historically, it was fatal
to several of them (&lt;tt class="docutils literal"&gt;fallocate&lt;/tt&gt; for cheap allocation of large files, and swap
over &lt;span class="caps"&gt;NFS&lt;/span&gt; did not work). Whilst these features &lt;em&gt;do&lt;/em&gt; work today, it&amp;#8217;s notable
that kernel support needed to be added for swap &lt;a class="footnote-reference" href="#swapnfs" id="footnote-reference-4"&gt;[4]&lt;/a&gt;, and the &lt;span class="caps"&gt;NFS&lt;/span&gt;
protocol extended specifically for &lt;tt class="docutils literal"&gt;fallocate&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Caching becomes tricky because files are complicated and messy things that have
data and &amp;#8220;meta-data&amp;#8221;. Here&amp;#8217;s an excerpt from the &lt;span class="caps"&gt;NFS&lt;/span&gt; mount&amp;nbsp;options:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;ac / noac&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Selects whether the client may cache file attributes. If neither option is
specified (or if &lt;tt class="docutils literal"&gt;ac&lt;/tt&gt; is specified), the client caches file&amp;nbsp;attributes.&lt;/p&gt;
&lt;p&gt;To improve performance, &lt;span class="caps"&gt;NFS&lt;/span&gt; clients cache file attributes. Every few
seconds, an &lt;span class="caps"&gt;NFS&lt;/span&gt; client checks the server&amp;#8217;s version of each file&amp;#8217;s
attributes for updates. Changes that occur on the server in those small
intervals remain undetected until the client checks the server again. The
&lt;tt class="docutils literal"&gt;noac&lt;/tt&gt; option prevents clients from caching file attributes so that
applications can more quickly detect file changes on the&amp;nbsp;server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yikes.&lt;/p&gt;
&lt;p&gt;The practice of using unlinked temporary files also works over &lt;span class="caps"&gt;NFS&lt;/span&gt; but again …
required &lt;a class="reference external" href="https://nfs.sourceforge.net/#faq_d2"&gt;workarounds&lt;/a&gt; because &lt;span class="caps"&gt;NFS&lt;/span&gt; is
stateless &lt;a class="footnote-reference" href="#simples" id="footnote-reference-5"&gt;[1]&lt;/a&gt; so open file handles locally do not correspond to open
file handles on the&amp;nbsp;server.&lt;/p&gt;
&lt;p&gt;All this doesn&amp;#8217;t matter too much when the portion of the virtual file-system
being mounted over &lt;span class="caps"&gt;NFS&lt;/span&gt; is relatively limited, as in the common case of mounting
user home directories over &lt;span class="caps"&gt;NFS&lt;/span&gt;. However, when the &lt;em&gt;entire&lt;/em&gt; root file-system is
&lt;span class="caps"&gt;NFS&lt;/span&gt;, quite a few applications can start having &amp;#8220;difficulty&amp;#8221;. One of the more
prominent, on Ubuntu especially, is snapd which &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/snapd/+bug/1884299"&gt;doesn&amp;#8217;t&lt;/a&gt; &lt;a class="reference external" href="https://bugs.launchpad.net/snapd/+bug/1973321"&gt;like&lt;/a&gt; running on an &lt;span class="caps"&gt;NFS&lt;/span&gt; root. Like
it or not, it&amp;#8217;s a pretty integral part of the Ubuntu eco-system at this point
(providing Firefox on the desktop and &lt;span class="caps"&gt;LXD&lt;/span&gt; on the server), so it would be nice
to have a netboot system that can support it too &lt;a class="footnote-reference" href="#docker" id="footnote-reference-6"&gt;[5]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Okay, we&amp;#8217;ve established there are some issues with running &lt;span class="caps"&gt;NFS&lt;/span&gt; as root. What&amp;#8217;re
the&amp;nbsp;alternatives?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="sometimes-i-m-up"&gt;
&lt;span id="skip-here"&gt;&lt;/span&gt;&lt;h2&gt;Sometimes I&amp;#8217;m&amp;nbsp;up&lt;/h2&gt;
&lt;p&gt;There are several daemons and protocols that support serving block devices over
the network, and they differ in some quite interesting&amp;nbsp;ways:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;iSCSI&lt;/dt&gt;
&lt;dd&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/ISCSI"&gt;iSCSI&lt;/a&gt; can be trivially summarized as: &lt;a class="reference external" href="https://en.wikipedia.org/wiki/SCSI"&gt;&lt;span class="caps"&gt;SCSI&lt;/span&gt;&lt;/a&gt; commands over &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Internet_protocol_suite"&gt;&lt;span class="caps"&gt;TCP&lt;/span&gt;/&lt;span class="caps"&gt;IP&lt;/span&gt;&lt;/a&gt;.
This is by &lt;em&gt;far&lt;/em&gt; the most popular method of serving block devices. It has
the advantages that it doesn&amp;#8217;t require expensive equipment (unlike &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Fibre_Channel"&gt;Fibre
Channel&lt;/a&gt;, its major competitor in the enterprise), and can be routed over
multiple networks (as it&amp;#8217;s built on &lt;span class="caps"&gt;IP&lt;/span&gt;). However, it&amp;#8217;s not entirely trivial
to configure (it is &lt;em&gt;very&lt;/em&gt; flexible, but for our purposes here I wanted
something simpler to start out&amp;nbsp;with).&lt;/dd&gt;
&lt;dt&gt;AoE&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/ATA_over_Ethernet"&gt;&lt;span class="caps"&gt;ATA&lt;/span&gt; over Ethernet&lt;/a&gt;. Like iSCSI, the name says it all. The client simply
passes &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Parallel_ATA"&gt;&lt;span class="caps"&gt;ATA&lt;/span&gt;&lt;/a&gt; disk commands over Ethernet to the server. Note that this
does indeed run over Ethernet &lt;em&gt;only&lt;/em&gt; (layer 2), &lt;em&gt;not&lt;/em&gt; &lt;span class="caps"&gt;TCP&lt;/span&gt;/&lt;span class="caps"&gt;IP&lt;/span&gt; so it&amp;#8217;s not
routable over the Internet, only local networks. It&amp;#8217;s very simple to set up
(moreso than iSCSI), but two things made me skip it here. The first is that
routing over layer 2 may be perfectly sufficient for many use-cases, but
there&amp;#8217;s several others where it&amp;#8217;ll be a limiting&amp;nbsp;factor.&lt;/p&gt;
&lt;p class="last"&gt;The second is that &lt;a class="reference external" href="https://launchpad.net/ubuntu/+source/aoetools"&gt;aoetools&lt;/a&gt;, the package for AoE in Ubuntu, is an
extremely &amp;#8220;mature&amp;#8221; package. Specifically, the upstream version in Ubuntu
hasn&amp;#8217;t changed since Xenial (16.04, 7 years ago at the time of writing).
That isn&amp;#8217;t to say it&amp;#8217;s bad or entirely unmaintained, but there&amp;#8217;s no active
work on it as best as I can tell (and that usually doesn&amp;#8217;t bode too well
from a security point of&amp;nbsp;view).&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;&lt;span class="caps"&gt;NBD&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Network_block_device"&gt;Network Block Devices&lt;/a&gt; differs in that doesn&amp;#8217;t implement the commands
from an existing disk protocol (&lt;span class="caps"&gt;SCSI&lt;/span&gt; or &lt;span class="caps"&gt;ATA&lt;/span&gt;). Instead, it uses its own
protocol which, at it&amp;#8217;s core, is almost laughably&amp;nbsp;simple.&lt;/p&gt;
&lt;p class="last"&gt;It also operates over &lt;span class="caps"&gt;TCP&lt;/span&gt;/&lt;span class="caps"&gt;IP&lt;/span&gt;, avoiding the layer 2 limitations of AoE, and
is an actively maintained project which optionally includes facilities for
&lt;span class="caps"&gt;TLS&lt;/span&gt; encryption. I won&amp;#8217;t be using those here, but you&amp;#8217;ll have some options
for better security down the&amp;nbsp;line.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Simplicity is another argument in favour of serving block devices instead of
file-systems. Compare the two&amp;nbsp;scenarios:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/nbd-vs.svg" type="image/svg+xml"&gt;&lt;/object&gt;
&lt;p&gt;They don&amp;#8217;t look that different but consider what serving files over a network
means, versus serving block devices. What operations can be performed against a
file? Opening, closing, reading, writing, truncating, locking, linking,
touching, the list goes on. All this &lt;em&gt;must&lt;/em&gt; be handled by the protocol to
implement even a bare-bones network file-system. The bare-bones case for block
devices (as noted above) is &lt;em&gt;radically&lt;/em&gt;&amp;nbsp;simpler.&lt;/p&gt;
&lt;p&gt;The baseline portion of the &lt;a class="reference external" href="https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md"&gt;&lt;span class="caps"&gt;NBD&lt;/span&gt; Protocol&lt;/a&gt; consists of commands to &amp;#8220;read some
bytes&amp;#8221;, &amp;#8220;write some bytes&amp;#8221;, and &amp;#8220;disconnect&amp;#8221;. That&amp;#8217;s it. There are some other
commands which may optionally tell the server to trim, flush, or cache blocks,
and some other messages for option negotiation at connection time, but the core
of the protocol really is &lt;em&gt;that simple&lt;/em&gt;. I like&amp;nbsp;simple.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="sometimes-i-m-down"&gt;
&lt;h2&gt;Sometimes I&amp;#8217;m&amp;nbsp;down&lt;/h2&gt;
&lt;p&gt;So far, we&amp;#8217;ve looked at how the root file-system will be handled when
netbooting. However, the root file-system only matters &lt;em&gt;after&lt;/em&gt; the Linux kernel
has started. How do we obtain the Linux kernel itself (and other sundry boot
resources) at system start? This involves neither &lt;span class="caps"&gt;NFS&lt;/span&gt; nor &lt;span class="caps"&gt;NBD&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;When netbooting, the Pi first requests an &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Internet_Protocol_version_4"&gt;IPv4&lt;/a&gt; address from the local
network via &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol"&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt;&lt;/a&gt;. The local router responds with a &lt;span class="caps"&gt;DHCP&lt;/span&gt; offer, and our
netboot server tacks a (minimal) &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Preboot_Execution_Environment"&gt;&lt;span class="caps"&gt;PXE&lt;/span&gt;&lt;/a&gt; boot menu on the end suggesting where
the client may find a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol"&gt;&lt;span class="caps"&gt;TFTP&lt;/span&gt;&lt;/a&gt; server for&amp;nbsp;booting.&lt;/p&gt;
&lt;p&gt;This is typical jargon-laden nonsense, so let&amp;#8217;s translate a&amp;nbsp;bit:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="19%" /&gt;
&lt;col width="29%" /&gt;
&lt;col width="24%" /&gt;
&lt;col width="27%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Jargon&lt;/th&gt;
&lt;th class="head"&gt;Raspberry Pi&lt;/th&gt;
&lt;th class="head"&gt;Router&lt;/th&gt;
&lt;th class="head"&gt;Netboot Server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt; &lt;span class="caps"&gt;DISCOVER&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;#8220;Hello? Can anybody
give me an IPv4
address? By the
way, I&amp;#8217;m a netboot
client&amp;#8221;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt; &lt;span class="caps"&gt;OFFER&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;#8220;Sure, would you
like to be
192.168.0.200?&amp;#8221;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt; Proxy
Option 43
&lt;span class="caps"&gt;PXE&lt;/span&gt; Boot Menu&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;#8220;By the way, for
&amp;#8216;Raspberry Pi Boot&amp;#8217;
see &lt;span class="caps"&gt;TFTP&lt;/span&gt; server at
192.168.0.4&amp;#8221;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt; &lt;span class="caps"&gt;REQUEST&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;#8220;Okay, I&amp;#8217;d like to
be 192.168.0.200,
please?&amp;#8221;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt; &lt;span class="caps"&gt;ACK&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;#8220;Right, you are
192.168.0.200 for
the next 12
hours, see me
again after that&amp;#8221;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;TFTP&lt;/span&gt; &lt;span class="caps"&gt;RRQ&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;#8220;Can you send me
the content of
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SERIAL&lt;/span&gt;/start.elf&lt;/tt&gt;,
please?&amp;#8221;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;TFTP&lt;/span&gt; &lt;span class="caps"&gt;OACK&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;#8220;Sure, it&amp;#8217;s going
to be 225065 bytes
long, and I&amp;#8217;ll send
it in chunks of
1468 bytes&amp;#8221;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The important things to note here are as&amp;nbsp;follows:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;We need a &lt;abbr title="Dynamic Host Configuration Protocol"&gt;&lt;span class="caps"&gt;DHCP&lt;/span&gt;&lt;/abbr&gt; server. This is
pretty much taken-as-read on any network these&amp;nbsp;days.&lt;/li&gt;
&lt;li&gt;We need a netboot server with a &lt;span class="caps"&gt;DHCP&lt;/span&gt; proxy and a &lt;abbr title="Trivial File Transfer Protocol"&gt;&lt;span class="caps"&gt;TFTP&lt;/span&gt;&lt;/abbr&gt; server. This is fairly simple. Any Ubuntu server can
install &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Dnsmasq"&gt;dnsmasq&lt;/a&gt; (if it hasn&amp;#8217;t already) to obtain&amp;nbsp;this.&lt;/li&gt;
&lt;li&gt;We need the Raspberry Pi&amp;#8217;s serial&amp;nbsp;number.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This last point may seem a bit strange, but it&amp;#8217;s because &lt;span class="caps"&gt;TFTP&lt;/span&gt; is, as the name
suggests, trivial. The protocol provides no means for the client to identify
itself to the server, so how are we to know which boot partition we should read
files&amp;nbsp;from?&lt;/p&gt;
&lt;p&gt;Identification by &lt;a class="reference external" href="https://en.wikipedia.org/wiki/MAC_address"&gt;&lt;span class="caps"&gt;MAC&lt;/span&gt;&lt;/a&gt; address is one possibility &lt;a class="footnote-reference" href="#macsecure" id="footnote-reference-7"&gt;[6]&lt;/a&gt;, but that&amp;#8217;s
not an option for us (unsupported by the &lt;span class="caps"&gt;TFTP&lt;/span&gt; server in dnsmasq). Instead we
rely upon the Pi identifying itself by the sequence of files it initially
attempts to request. When netbooting, a Pi (more specifically a Pi 4 or later)
will attempt the following sequence of files &lt;a class="footnote-reference" href="#simples" id="footnote-reference-8"&gt;[1]&lt;/a&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SERIAL&lt;/span&gt;/start4.elf&lt;/tt&gt;, e.g. &lt;tt class="docutils literal"&gt;1234abcd/start4.elf&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SERIAL&lt;/span&gt;/start.elf&lt;/tt&gt;, e.g. &lt;tt class="docutils literal"&gt;1234abcd/start.elf&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;start.elf&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the bootloader finds files with the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SERIAL&lt;/span&gt;/&lt;/tt&gt; &lt;a class="footnote-reference" href="#serial" id="footnote-reference-9"&gt;[7]&lt;/a&gt; prefix, all
subsequent requests will also have that prefix, allowing us to easily determine
which &lt;span class="caps"&gt;OS&lt;/span&gt; image files should be served&amp;nbsp;from.&lt;/p&gt;
&lt;p&gt;The Pi then proceeds to request (over &lt;span class="caps"&gt;TFTP&lt;/span&gt;):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The rest of the tertiary bootloader (e.g. &lt;tt class="docutils literal"&gt;start4.elf&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;fixup4.dat&lt;/tt&gt;, and
its configuration files like &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;The device-tree for the specific board (e.g. &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;bcm2711-rpi-4-b.dtb&lt;/span&gt;&lt;/tt&gt;) and any
overlays required by the configuration, or by devices that are plugged in
(e.g. &lt;tt class="docutils literal"&gt;overlays/dwc2.dtbo&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;overlays/ov5647.dtbo&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;The kernel and initramfs requested by the configuration, and its command line
(e.g. &lt;tt class="docutils literal"&gt;vmlinuz&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;initrd.img&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With all this loaded into appropriate locations in memory, the bootloader hands
over to the Linux kernel, which mounts the initramfs as its initial root
file-system, and launches the &lt;tt class="docutils literal"&gt;/init&lt;/tt&gt; binary within&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;In the case of Ubuntu, this is the usual initramfs you&amp;#8217;ll find on pretty much
any Ubuntu installation. It&amp;#8217;ll search the kernel command line for the &amp;#8220;real&amp;#8221;
root device, attempt to mount it, and &amp;#8220;pivot&amp;#8221; the root that&amp;nbsp;mount.&lt;/p&gt;
&lt;p&gt;For the &lt;span class="caps"&gt;NFS&lt;/span&gt; case, the kernel command line would include something like
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nfsroot=server:/exports/ubuntu-jammy&lt;/span&gt; &lt;span class="pre"&gt;root=/dev/nfs&lt;/span&gt;&lt;/tt&gt;. For the &lt;span class="caps"&gt;NBD&lt;/span&gt; case, the
kernel command line would include something like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nbdroot=server/ubuntu-jammy&lt;/span&gt;
&lt;span class="pre"&gt;root=/dev/nbd0p2&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;At this point you should have a basic understanding of the Pi&amp;#8217;s netboot
process. Let&amp;#8217;s explore what the server side configuration for &lt;span class="caps"&gt;TFTP&lt;/span&gt;, &lt;span class="caps"&gt;NFS&lt;/span&gt;, and
&lt;span class="caps"&gt;NBD&lt;/span&gt; can look like from a high level. We&amp;#8217;ll get into specifics in the next post;
this is just to give you an idea of the considerations and possibilities&amp;nbsp;involved.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="glory-hallelujah"&gt;
&lt;h2&gt;Glory,&amp;nbsp;hallelujah!&lt;/h2&gt;
&lt;p&gt;A typical server &lt;span class="caps"&gt;TFTP&lt;/span&gt; configuration (whether subsequently &lt;span class="caps"&gt;NFS&lt;/span&gt;, &lt;span class="caps"&gt;NBD&lt;/span&gt;, or anything
else) is to have the boot partition of an &lt;span class="caps"&gt;OS&lt;/span&gt; image unpacked or mounted under a
particular path, and then make a symlink from the Raspberry Pi&amp;#8217;s serial number
to that path. Re-writing the symlink is then enough to switch which Pi boots
which&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;While this much does not differ between the &lt;span class="caps"&gt;NFS&lt;/span&gt; and &lt;span class="caps"&gt;NBD&lt;/span&gt; cases, there is the
question of how to make the boot files&amp;nbsp;available.&lt;/p&gt;
&lt;p&gt;In the case of &lt;span class="caps"&gt;NFS&lt;/span&gt;, as the server is serving a file-system it is typical to
simply unpack the entire &lt;span class="caps"&gt;OS&lt;/span&gt; image, both the root and boot file-systems, into a
directory and call that the &amp;#8220;image&amp;#8221; that is served. The symlink for the Pi&amp;#8217;s
serial number then points to the &lt;tt class="docutils literal"&gt;/boot/firmware&lt;/tt&gt; directory within the
unpacked&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;For example, if we have two &lt;span class="caps"&gt;OS&lt;/span&gt; images, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-jammy&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-mantic&lt;/span&gt;&lt;/tt&gt;,
and two Raspberry Pis with serial numbers &lt;tt class="docutils literal"&gt;1234abcd&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;4567cdef&lt;/tt&gt; we
might lay out our files like&amp;nbsp;so:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/
├─ …
├─ srv/
│  ├─ ubuntu-jammy/
│  │  ├─ bin/
│  │  ├─ boot/
│  │  │  ├─ firmware/
│  │  │  └─ …
│  │  └─ …
│  ├─ ubuntu-mantic/
│  │  ├─ bin/
│  │  ├─ boot/
│  │  │  ├─ firmware/
│  │  │  └─ …
│  │  └─ …
│  └─ boot/
│     ├─ 1234abcd-&amp;gt;/srv/ubuntu-jammy/boot/firmware
│     └─ 4567cdef-&amp;gt;/srv/ubuntu-mantic/boot/firmware
└─ …
&lt;/pre&gt;
&lt;p&gt;The two images are completely unpacked under &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/srv/ubuntu-jammy&lt;/span&gt;&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/srv/ubuntu-mantic&lt;/span&gt;&lt;/tt&gt;, then symlinks under &lt;tt class="docutils literal"&gt;/srv/boot&lt;/tt&gt; point to the
&lt;tt class="docutils literal"&gt;/boot/firmware&lt;/tt&gt; directories of the unpacked images. Our &lt;span class="caps"&gt;TFTP&lt;/span&gt; server is
configured to serve &lt;tt class="docutils literal"&gt;/srv/boot&lt;/tt&gt;, and our &lt;span class="caps"&gt;NFS&lt;/span&gt; server to export &lt;tt class="docutils literal"&gt;/srv&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;The advantages are that it&amp;#8217;s a relatively simple setup, requiring no special
mounts on the server side, and that (assuming all unpacked images are in the
same file-system on the server), all available space is shared between all
netbooting Pis. The disadvantage (other than it being an &lt;span class="caps"&gt;NFS&lt;/span&gt; boot setup) is
that all available space is shared between all netbooting Pis so one Pi can use
up all available space for everyone, unless additional steps like quotas are&amp;nbsp;taken.&lt;/p&gt;
&lt;p&gt;In the case of &lt;span class="caps"&gt;NBD&lt;/span&gt;, which we&amp;#8217;ll explore more in the next post, I would suggest
a simple setup is to leave the &lt;span class="caps"&gt;OS&lt;/span&gt; image as it is (in a file) and simply expand
the file to the desired size. A loop device can be created for the image file,
with a partition scan to find the boot partition, which can then be mounted. I
would still recommend using a symlink to point to the mount for ease of
changing&amp;nbsp;later.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s consider the scenario from before (two images, jammy and mantic, two Pis
with known serial numbers) in this&amp;nbsp;setup:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/
├─ …
├─ srv/
│  ├─ ubuntu-jammy.img
│  ├─ ubuntu-mantic.img
│  ├─ mnt/
│  │  ├─ ubuntu-jammy/  (mount of ubuntu-jammy.img partition 1)
│  │  └─ ubuntu-mantic/  (mount of ubuntu-mantic.img partition 1)
│  └─ boot/
│     ├─ 1234abcd-&amp;gt;/srv/mnt/ubuntu-jammy
│     └─ 4567cdef-&amp;gt;/srv/mnt/ubuntu-mantic
└─ …
&lt;/pre&gt;
&lt;p&gt;The two images are simply placed under &lt;tt class="docutils literal"&gt;/srv&lt;/tt&gt;. Loop devices are created of
each, and the first partition mounted under appropriate directories under
&lt;tt class="docutils literal"&gt;/srv/mnt&lt;/tt&gt;. Symlinks under &lt;tt class="docutils literal"&gt;/srv/boot&lt;/tt&gt; link Pi serial numbers to the
appropriate mount. Our &lt;span class="caps"&gt;TFTP&lt;/span&gt; server is configured to serve &lt;tt class="docutils literal"&gt;/srv/boot&lt;/tt&gt; as
before, and our &lt;span class="caps"&gt;NBD&lt;/span&gt; server is configured to export each &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/srv/*.img&lt;/span&gt;&lt;/tt&gt; file as
a block&amp;nbsp;device.&lt;/p&gt;
&lt;p&gt;The advantages are that this is a trivial setup (the image doesn&amp;#8217;t even need
unpacking), and that each image is necessarily limited to its own storage. The
disadvantage is that storage isn&amp;#8217;t shared between images. However, we saw in
the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/building-blocks.html"&gt;prior post&lt;/a&gt; that there&amp;#8217;s all
manner of things we can do with block devices, so we&amp;#8217;ll explore some
possibilities there in a future post&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;I should warn there&amp;#8217;s also a nicely hidden, but quite serious issue with this
set up which we&amp;#8217;ll look at next time when we actually build it (and then fix
it … obviously). Anyway, that&amp;#8217;s all for&amp;nbsp;now!&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="simples" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;[1]&lt;/td&gt;&lt;td&gt;&lt;em&gt;(&lt;a class="fn-backref" href="#footnote-reference-1"&gt;1&lt;/a&gt;, &lt;a class="fn-backref" href="#footnote-reference-5"&gt;2&lt;/a&gt;, &lt;a class="fn-backref" href="#footnote-reference-8"&gt;3&lt;/a&gt;)&lt;/em&gt; This is a gross over-simplification (or an outright lie), but
serves to make the point.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="btrfs" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Alright, some of them don&amp;#8217;t or only recently added the
functionality, or it&amp;#8217;s still experimental (&lt;em&gt;cough&lt;/em&gt; btrfs), but they&amp;#8217;re
usually the file-systems that have their own rules for block mapping.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="neversimple" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Caching is never simple.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="swapnfs" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;No other file system has a &amp;#8220;make swap work on this file-system&amp;#8221;
&lt;a class="reference external" href="https://cateee.net/lkddb/web-lkddb/NFS_SWAP.html"&gt;kernel config&lt;/a&gt; item&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="docker" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I&amp;#8217;m told Docker overlays also have issues with &lt;span class="caps"&gt;NFS&lt;/span&gt; but it&amp;#8217;s not
something I&amp;#8217;ve played with directly and I&amp;#8217;ve not had the time to verify it
that for this article.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="macsecure" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-7"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Though not for &amp;#8220;security&amp;#8221; (&lt;a class="reference external" href="https://en.wikipedia.org/wiki/MAC_address"&gt;MACs&lt;/a&gt; can be trivially
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/MAC_spoofing"&gt;spoofed&lt;/a&gt;)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="serial" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-9"&gt;[7]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SERIAL&lt;/span&gt;&lt;/tt&gt; portion is the serial number of the Pi (which can
be found at the end of the output of &lt;tt class="docutils literal"&gt;cat /proc/cpuinfo&lt;/tt&gt;), in lower-case
hexidecimal format. If the serial number is longer than 8 characters, only
the last 8 characters are used.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="net"></category><category term="nbd"></category></entry><entry><title>Building Blocks</title><link href="https://waldorf.waveform.org.uk/2023/building-blocks.html" rel="alternate"></link><published>2023-10-24T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-10-24:/2023/building-blocks.html</id><summary type="html">&lt;p class="first last"&gt;In which we finally cover what a block device&amp;nbsp;is&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In an earlier series of posts (Playing with Blocks &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;1&lt;/a&gt;, &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;2&lt;/a&gt;, &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2022/playing-with-blocks-its-all-connected.html"&gt;3&lt;/a&gt;, and &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/playing-with-blocks-reluks.html"&gt;4&lt;/a&gt;) we had a
look at customizing Ubuntu images for the Raspberry Pi by playing around with
various block device transformations. However, we never covered &lt;em&gt;what&lt;/em&gt; a block
device actually &lt;em&gt;is&lt;/em&gt;. This is going to be a vaguely important topic in an
upcoming series of posts on &lt;abbr title="Network Block Device"&gt;&lt;span class="caps"&gt;NBD&lt;/span&gt;&lt;/abbr&gt; booting of
Ubuntu, so I figured some brief coverage of the topic is&amp;nbsp;warranted.&lt;/p&gt;
&lt;p&gt;Brief.&lt;/p&gt;
&lt;p&gt;Who am I&amp;nbsp;kidding?&lt;/p&gt;
&lt;div class="section" id="blockage"&gt;
&lt;h2&gt;Blockage&lt;/h2&gt;
&lt;p&gt;In the &lt;span class="caps"&gt;UNIX&lt;/span&gt; world a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Device_file"&gt;block device&lt;/a&gt; is a very simple concept: it&amp;#8217;s a storage
device which provides access to its contents as an ordered sequence of
identically sized blocks. That&amp;#8217;s it. No files, no directories, no permissions,
nothing more than a numbered sequence of blocks all with the same size.
Typically, the blocks are some whole multiple of an underlying device&amp;#8217;s sector
size, which over most of computing history has been 512 bytes. However, the
user may access block devices byte-by-byte if they&amp;nbsp;wish:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-ops.svg" type="image/svg+xml"&gt;&lt;/object&gt;
&lt;p&gt;For instance, a read of a few bytes (illustrated on the left in green) may read
a full 512-byte sector, throw away the bytes outside the requested range, and
return that which was requested &lt;a class="footnote-reference" href="#cache1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. A write of a few bytes (illustrated
in the middle in red) is a bit more complex; the kernel needs to read the full
sector from the underlying device, change those bytes we requested to write in
the middle, then write the whole sector back to the underlying device
&lt;a class="footnote-reference" href="#cache2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Crucially, block devices are &lt;em&gt;random&lt;/em&gt; access. We can access any block,
anywhere, at any time, without having to access all blocks prior to it. In
other words, block devices make storage look and &amp;#8220;feel&amp;#8221; like &lt;abbr title="Random Access Memory"&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/abbr&gt;. This is as opposed to the &amp;#8220;byte-stream&amp;#8221; view of files which
owes more to the tape drives of old (&amp;#8220;seeking&amp;#8221; the head to a particular
location, then reading or writing from that&amp;nbsp;point).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="transform"&gt;
&lt;h2&gt;Transform!&lt;/h2&gt;
&lt;p&gt;The simplicity of block devices leads to them being wonderfully flexible things
that can often be converted into new block devices in useful ways. Probably the
most basic block device transformation that everyone is familiar with is
&amp;#8220;partitioning&amp;#8221;, which simply sub-divides one block device into multiple new
ones (minus some header space). This transformation effectively just re-numbers
the blocks but doesn&amp;#8217;t change their content or&amp;nbsp;ordering.&lt;/p&gt;
&lt;p&gt;More complex transformations are also&amp;nbsp;available.&lt;/p&gt;
&lt;p&gt;The &lt;abbr title="Logical Volume Manager"&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;&lt;/abbr&gt; sub-system (&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;explored previously&lt;/a&gt;) gathers multiple block devices into a &amp;#8220;volume group&amp;#8221;. The
volume group can then be used to produce other block devices called &amp;#8220;logical
volumes&amp;#8221;. These are similar to partitions, but have no need to be contiguous on
the underlying block devices, and can even span multiple underlying&amp;nbsp;devices.&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-transforms.svg" type="image/svg+xml"&gt;A drive (sda) is divided into two partitions (sda1, sda2) the latter
of which is further divided into three block devices with &lt;span class="caps"&gt;LVM&lt;/span&gt; (root,
home, and swap). The home device has been expanded at some point in
its history as its storage is non-contiguous&lt;/object&gt;
&lt;p&gt;Speaking of multiple underlying devices, &lt;abbr title="Redundant Array of Inexpensive Disks"&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt;&lt;/abbr&gt; systems (like &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Mdadm"&gt;mdadm&lt;/a&gt;) consume multiple block devices and
produce a single block device. The blocks in the produced device are duplicated
to the underlying block devices (in the simplest mirror case; more complex
transforms are involved in things like &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Standard_RAID_levels"&gt;&lt;span class="caps"&gt;RAID5&lt;/span&gt;&lt;/a&gt;). Corruption
or even loss of one of the underlying block devices, as in the failure of a
drive, can be entirely hidden from the consumer of the produced block&amp;nbsp;device.&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-raid.svg" type="image/svg+xml"&gt;Four drives (sda, sdb, sdc, and sdd) are partitioned such that each
has a small initial partition (sda1 etc.) and a larger partition
(sda2 etc.); the small initial partitions are combined as a &lt;span class="caps"&gt;RAID1&lt;/span&gt;
mirror into a boot block device. The larger partitions are combined
as a &lt;span class="caps"&gt;RAID10&lt;/span&gt; device (a stripe of mirrors) as a root device.&lt;/object&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup"&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/a&gt; encryption mechanism (also explored in &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;previous posts&lt;/a&gt;) consumes one block device and produces a slightly smaller one
(minus the size of the &lt;span class="caps"&gt;LUKS&lt;/span&gt; header) that scrambles (and unscrambles) the
content of blocks as they are written (and&amp;nbsp;read).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-hierarchy"&gt;
&lt;h2&gt;The&amp;nbsp;Hierarchy&lt;/h2&gt;
&lt;p&gt;A &lt;a class="reference external" href="https://en.wikipedia.org/wiki/File_system"&gt;file-system&lt;/a&gt; is just another kind of transformation of a block device; one
which converts the flat sequence of blocks into a tree-like hierarchy of files
(optionally with attributes like ownership, groups, permissions, and all the
rest of that&amp;nbsp;gubbins).&lt;/p&gt;
&lt;p&gt;File systems can be relatively simple affairs, like the ancient but ubiquitous
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system"&gt;&lt;span class="caps"&gt;FAT&lt;/span&gt;&lt;/a&gt; which has no concept of file ownership, &lt;span class="caps"&gt;UNIX&lt;/span&gt; modes, and no symlinks.
Alternatively they can be complex, like the common &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Ext4"&gt;ext4&lt;/a&gt; file-system which
has journaling data recovery, the full suite of &lt;span class="caps"&gt;UNIX&lt;/span&gt; ownership, attributes, and
linkage, and numerous tunable&amp;nbsp;options.&lt;/p&gt;
&lt;p&gt;Ultimately, the file-systems produced from various block devices are all
grafted together (&amp;#8220;mounted&amp;#8221; in &lt;span class="caps"&gt;UNIX&lt;/span&gt; parlance) onto a &amp;#8220;virtual&amp;#8221; file-system
which is what all userland processes (and you, the user) generally consider
&amp;#8220;the file-system&amp;#8221;; the place you find all the files on your&amp;nbsp;computer.&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-to-fs.svg" type="image/svg+xml"&gt;Two partitions (sda1 and sda2) are formatted as &lt;span class="caps"&gt;FAT&lt;/span&gt; and ext4
respectively. The vfat driver converts the sda1 block device into a
typical boot file-system for the Pi (containing bootcode.bin,
start4.elf, config.txt, etc). The ext4 driver converts the sda2 block
device into a typical Linux root file-system (containing bin, etc,
lib, usr, sbin directories). The virtual file-system then &amp;#8220;mounts&amp;#8221;
the ext4 hierarchy under / and the &lt;span class="caps"&gt;FAT&lt;/span&gt; hierarchy under /boot/firmware&lt;/object&gt;
&lt;p&gt;A common Linux storage layout is shown above. The drive (&lt;tt class="docutils literal"&gt;/dev/sda&lt;/tt&gt;) has two
partitions. The first partition (&lt;tt class="docutils literal"&gt;/dev/sda1&lt;/tt&gt;) is formatted as &lt;span class="caps"&gt;FAT&lt;/span&gt;, which the
kernel&amp;#8217;s vfat driver transforms into a file-system. The second partition
(&lt;tt class="docutils literal"&gt;/dev/sda2&lt;/tt&gt;) is formatted as ext4, which the ext4 driver transforms into
another file-system. The kernel has mounted the ext4 file-system as the root of
the &amp;#8220;virtual&amp;#8221; file-system, and the &lt;span class="caps"&gt;FAT&lt;/span&gt; file-system under the &lt;tt class="docutils literal"&gt;/boot/firmware&lt;/tt&gt;
mount-point.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="holy-cow"&gt;
&lt;h2&gt;Holy&amp;nbsp;CoW!&lt;/h2&gt;
&lt;p&gt;Some modern file-systems provide a copy-on-write facility (most notably
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Btrfs"&gt;btrfs&lt;/a&gt; and &lt;a class="reference external" href="https://en.wikipedia.org/wiki/ZFS"&gt;zfs&lt;/a&gt;). This allows rapid (near instantaneous) cloning of an
entire file-system hierarchy. The clone doesn&amp;#8217;t initially take any storage of
its own, and reads of the clone actually go to the original files. However,
writes to the clone allocate new storage for the changed&amp;nbsp;content.&lt;/p&gt;
&lt;p&gt;However, the facility is not limited to modern file-systems. &lt;span class="caps"&gt;LVM&lt;/span&gt; also provides
such a facility, initially intended to provide snapshots of file-systems at a
point in time (for backup, testing, or other&amp;nbsp;purposes).&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-cow-snap.svg" type="image/svg+xml"&gt;The &amp;#8220;root&amp;#8221; block device contains a number of filled blocks in green,
while the &amp;#8220;snap&amp;#8221; block device has an equivalent number of &amp;#8220;empty&amp;#8221;
blocks. Two reads, indicated by arrows, are made against the &amp;#8220;snap&amp;#8221;
device but pass through to the underlying &amp;#8220;root&amp;#8221; device. Below this
an equivalent layout is show, but now one of the &amp;#8220;filled&amp;#8221; blocks is
&amp;#8220;red&amp;#8221; indicating a change in content. Above it, the equivalent block
in the &amp;#8220;snap&amp;#8221; device is green indicating it contains the original
content. A read against this block comes directly from the &amp;#8220;snap&amp;#8221;
device instead of passing through to the underlying &amp;#8220;root&amp;#8221; device.&lt;/object&gt;
&lt;p&gt;The illustration above shows a &amp;#8220;root&amp;#8221; block device, presumably containing a
root file-system. We create a snapshot of the &amp;#8220;root&amp;#8221; block device called
&amp;#8220;snap&amp;#8221;. Initially, the &amp;#8220;snap&amp;#8221; block device is empty, and reads to it pass
through to the underlying &amp;#8220;root&amp;#8221; block device. However, when a block is the
&amp;#8220;root&amp;#8221; device is written to, its original content is first copied to the &amp;#8220;snap&amp;#8221;
device. Subsequent reads of this block in the &amp;#8220;snap&amp;#8221; device will go to this
copied block instead. Further writes to the same block in the &amp;#8220;root&amp;#8221; device are
ignored because the &amp;#8220;snap&amp;#8221; device only cares about the content at the point in
time it was&amp;nbsp;created.&lt;/p&gt;
&lt;p&gt;Note that one of the beauties of this system is that &lt;em&gt;we don&amp;#8217;t care&lt;/em&gt; what
file-system is on the &amp;#8220;root&amp;#8221; device. It could be &lt;span class="caps"&gt;FAT&lt;/span&gt;, ext4, &lt;span class="caps"&gt;XFS&lt;/span&gt;, even
(redundantly) one of the more modern systems that supports this internally.
However, a limitation of this snapshot system is that we need to allocate space
for it up front &lt;a class="footnote-reference" href="#notall" id="footnote-reference-3"&gt;[4]&lt;/a&gt;. Can we come up with something in which the snapshot
allocates blocks&amp;nbsp;dynamically?&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-cow-thin.svg" type="image/svg+xml"&gt;At the top, the &amp;#8220;image&amp;#8221; block device (read-only) contains a number
of filled blocks in green. Above it, a clone, &amp;#8220;clone1&amp;#8221;, exists with
the same number of blocks but all are empty. A read of two blocks in
&amp;#8220;clone1&amp;#8221;, indicated by arrows, passes through to the corresponding
blocks in &amp;#8220;image&amp;#8221;. Below, the one of the blocks in &amp;#8220;clone1&amp;#8221; is now
red indicating it has been changed from the original content in
&amp;#8220;image&amp;#8221;. Another arrow, indicating a read of that block now comes
straight from &amp;#8220;clone1&amp;#8221; and doesn&amp;#8217;t pass through. Another clone,
&amp;#8220;clone2&amp;#8221;, is below it with entirely empty blocks. A read of the same
block which is changed in &amp;#8220;clone1&amp;#8221;, passes through &amp;#8220;clone2&amp;#8221; to the
underlying &amp;#8220;image&amp;#8221; device.&lt;/object&gt;
&lt;p&gt;The illustration above shows &amp;#8220;thinly provisioned&amp;#8221; snapshots in &lt;span class="caps"&gt;LVM&lt;/span&gt;. This
operates a little like the regular snapshots in reverse. An underlying block
device called &amp;#8220;image&amp;#8221; presumably contains an &lt;span class="caps"&gt;OS&lt;/span&gt; image. We have created a
&amp;#8220;thinly provisioned&amp;#8221; snapshot of this called &amp;#8220;clone1&amp;#8221;. Reads of an unchanged
block pass through to the underlying (read-only) &amp;#8220;image&amp;#8221;. Writes to blocks in
&amp;#8220;clone1&amp;#8221; occur within its storage, with subsequent reads to that block no
longer passing through. Finally, we create another clone, &amp;#8220;clone2&amp;#8221;, which reads
the original block from the underlying&amp;nbsp;&amp;#8220;image&amp;#8221;.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s worth noting that most of the blocks in the clones are empty. Provided
they stay that way (i.e. the clones don&amp;#8217;t change too many blocks of the
underlying image), there&amp;#8217;s little sense in &lt;em&gt;actually&lt;/em&gt; allocating them. This is
the &amp;#8220;thin&amp;#8221; in &amp;#8220;thinly provisioned&amp;#8221;. The blocks of the clones are allocated on
demand so a clone initially takes up no space. This allocate-on-demand facility
can implement one form of &amp;#8220;over provisioning&amp;#8221; of storage. Each clone &lt;em&gt;thinks&lt;/em&gt;
it has all the storage allocated to the original image, but there&amp;#8217;s no need to
have all the storage available (unless the clones grow to require it,&amp;nbsp;naturally).&lt;/p&gt;
&lt;p&gt;Hopefully this gives some idea of how modern cloud systems can spin up clones
of an &lt;span class="caps"&gt;OS&lt;/span&gt; image nearly instantaneously, and how they can seemingly provide many
gigabytes of storage to containers or VMs, without necessarily having installed
all the storage they apparently&amp;nbsp;provide.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="layer-cake"&gt;
&lt;h2&gt;Layer&amp;nbsp;Cake&lt;/h2&gt;
&lt;p&gt;Finally, a brief illustration of just how silly this can get. Here&amp;#8217;s a
diagram of a storage layout I&amp;#8217;ve used on servers in the&amp;nbsp;past:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-stack.svg" type="image/svg+xml"&gt;A spiders web of storage. Four boxes represent four hard at the
bottom. Above this a larger box represents the &lt;span class="caps"&gt;RAID&lt;/span&gt; device built from
the drives. Above this a hexagonal volume group is built from the
large &lt;span class="caps"&gt;RAID&lt;/span&gt; device. From the volume group spring numerous block
devices, for &amp;#8220;tmp&amp;#8221;, &amp;#8220;root&amp;#8221;, &amp;#8220;home&amp;#8221;, and &amp;#8220;images&amp;#8221;. From the first
three spring regular file-systems, but from &amp;#8220;images&amp;#8221; spring several
more block devices named &amp;#8220;base&amp;#8221; and from that &amp;#8220;munin&amp;#8221;, &amp;#8220;docs&amp;#8221;, and
&amp;#8220;git&amp;#8221; (&lt;span class="caps"&gt;OS&lt;/span&gt; images running in containers).&lt;/object&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Four hard drives exist, each containing a small boot partition and a large
partition occupying the rest of the&amp;nbsp;capacity&lt;/li&gt;
&lt;li&gt;The four boot partitions are combined with a &lt;span class="caps"&gt;RAID1&lt;/span&gt; mirror into a boot block
device, &amp;#8220;md1&amp;#8221; &lt;a class="footnote-reference" href="#mirror" id="footnote-reference-4"&gt;[3]&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &amp;#8220;md1&amp;#8221; block device is formatted as &lt;span class="caps"&gt;FAT&lt;/span&gt; and contains the &amp;#8220;/boot&amp;#8221;&amp;nbsp;hierarchy.&lt;/li&gt;
&lt;li&gt;The four large partitions are combined with &lt;span class="caps"&gt;RAID5&lt;/span&gt; into a large block device,
&amp;#8220;md2&amp;#8221; (&lt;span class="caps"&gt;RAID5&lt;/span&gt; implies it has the capacity of n-1, i.e. three, of the
underlying devices, and can survive the loss of any one&amp;nbsp;device).&lt;/li&gt;
&lt;li&gt;The &amp;#8220;md2&amp;#8221; device is consumed by the &amp;#8220;raidvg&amp;#8221; volume group from which are
produced various block devices: the &amp;#8220;root&amp;#8221;, &amp;#8220;home&amp;#8221;, &amp;#8220;tmp&amp;#8221;, and &amp;#8220;images&amp;#8221;&amp;nbsp;volumes.&lt;/li&gt;
&lt;li&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;root&amp;#8221;, &amp;#8220;home&amp;#8221;, and &amp;#8220;tmp&amp;#8221; are formatted with a mix of file-systems, ext4 and
&lt;span class="caps"&gt;XFS&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;The &amp;#8220;images&amp;#8221; volume is a &amp;#8220;thin pool&amp;#8221; from which the &amp;#8220;base&amp;#8221; volume is&amp;nbsp;derived.&lt;/li&gt;
&lt;li&gt;The &amp;#8220;munin&amp;#8221;, &amp;#8220;docs&amp;#8221;, and &amp;#8220;git&amp;#8221; volumes are all thinly provisioned snapshots
of the &amp;#8220;base&amp;#8221;&amp;nbsp;volume.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This may seem like a complicated stack of transformations, but consider that in
essence most amount to little more than re-numbering and re-ordering of blocks
(the &lt;span class="caps"&gt;RAID5&lt;/span&gt; transform is a little more involved, admittedly). Hence, for very
little performance cost, we&amp;#8217;ve got redundancy (&lt;span class="caps"&gt;RAID&lt;/span&gt;), flexible volume creation
system with snapshotting (&lt;span class="caps"&gt;LVM&lt;/span&gt;), and copy-on-write cloning for VMs or containers
launched on the server (thin&amp;nbsp;provisioning).&lt;/p&gt;
&lt;p&gt;By this point you should have a reasonable understanding of what a block device
is, and what can be accomplished with the various transforms that are available
for them under Linux. Next time we&amp;#8217;ll take a look at the issues with netbooting
from &lt;span class="caps"&gt;NFS&lt;/span&gt; and how &lt;span class="caps"&gt;NBD&lt;/span&gt; (or block devices in general) can mitigate some of these&amp;nbsp;issues.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="cache1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In practice it&amp;#8217;s unlikely the bytes will actually be thrown away;
the sector requested will be stored in a cache, in case a future read
requests more bytes from the same sector (very likely when reading through a
file sequentially).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="cache2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Again, the changed block will wind up in a cache so future
modifications can skip the initial read step.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="mirror" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Simple &lt;span class="caps"&gt;RAID1&lt;/span&gt; mirroring is used to ensure that the &lt;span class="caps"&gt;BIOS&lt;/span&gt; can use any
of the drives as its initial boot device without caring about the &lt;span class="caps"&gt;RAID&lt;/span&gt;
layout (which it doesn&amp;#8217;t understand). &lt;em&gt;Note&lt;/em&gt;: This carries risks as it means
the &lt;span class="caps"&gt;BIOS&lt;/span&gt; and bootloader must treat the boot partition as strictly read-only
(and that&amp;#8217;s not necessarily guaranteed).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="notall" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The snapshot doesn&amp;#8217;t require all the space to be allocated, as the
diagram suggests. In fact, assuming the underlying device changes
infrequently, it&amp;#8217;s common to only allocate ~5% of the origin&amp;#8217;s storage.
However, you do still need to provide &lt;em&gt;some&lt;/em&gt; up front.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="storage"></category><category term="lvm"></category><category term="luks"></category></entry><entry><title>Making Mantic Magic</title><link href="https://waldorf.waveform.org.uk/2023/making-mantic-magic.html" rel="alternate"></link><published>2023-10-12T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-10-12:/2023/making-mantic-magic.html</id><summary type="html">&lt;p class="first last"&gt;Everything you (don&amp;#8217;t) need to do on Mantic to get the most out of
the Pi&amp;nbsp;5&lt;/p&gt;
</summary><content type="html">&lt;div class="figure"&gt;
&lt;img alt="A screenshot of the Ubuntu Mantic desktop, showing the Firefox web browser, a terminal, and the selection of Mantic wallpapers in the settings application in the foreground." src="https://waldorf.waveform.org.uk/images/mantic-screenshot.png" /&gt;
&lt;p class="caption"&gt;Meet the&amp;nbsp;Minotaur!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;#8217;s around this time of year that a &lt;a class="reference external" href="https://discourse.ubuntu.com/t/mantic-minotaur-release-notes/35534"&gt;new Ubuntu release&lt;/a&gt; comes out (including
images for the Raspberry Pi), and shortly afterwards I pen something on the
subject of how best to tweak it to get the most out of it on your Pi. Past
subjects have included adding a swap file … but that&amp;#8217;s now done out of the box.
Or enabling the &amp;#8220;zswap&amp;#8221; facility … but that&amp;#8217;s now on by default too. Erm …
let&amp;#8217;s see, how to flash the image to an &lt;abbr title="Solid State Drive"&gt;&lt;span class="caps"&gt;SSD&lt;/span&gt;&lt;/abbr&gt;? Nope,
that&amp;#8217;s as easy as an &lt;span class="caps"&gt;SD&lt;/span&gt; card these days, thanks to &lt;a class="reference external" href="https://www.raspberrypi.com/software/"&gt;rpi-imager&lt;/a&gt;. Disabling
multipath to gain a bit more &lt;span class="caps"&gt;RAM&lt;/span&gt; on the Pi Zero? Nope, default now&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;The list is indeed shrinking fast, but it&amp;#8217;s not completely empty yet and the
arrival of the Pi 5 has certainly left a &lt;em&gt;little&lt;/em&gt; opportunity for post-release&amp;nbsp;tweakage!&lt;/p&gt;
&lt;p&gt;Speaking of the 5&amp;nbsp;…&lt;/p&gt;
&lt;div class="section" id="presto"&gt;
&lt;h2&gt;Presto!&lt;/h2&gt;
&lt;p&gt;What&amp;#8217;s it like? How is the Pi 5, and how is Ubuntu on the Pi&amp;nbsp;5?&lt;/p&gt;
&lt;p&gt;Simply put, it&amp;#8217;s an awesome piece of Pi. I&amp;#8217;ll go so far as to say it&amp;#8217;s the
first Pi where, if your computing needs are fairly typical, it will meet them
as well as any cheap &lt;span class="caps"&gt;PC&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;I use Pis for pretty much all my work. All my coding, packaging, emailing,
talking to colleagues online, and &lt;em&gt;much of&lt;/em&gt; my browsing is done on a Pi 4. But
there it is: &amp;#8220;much of&amp;#8221;. Not &amp;#8220;all of&amp;#8221;. Playing videos on Youtube? Painful. A
video call involving a dozen colleagues? Out of the question. For those I turn
to a &lt;span class="caps"&gt;PC&lt;/span&gt;. Often at home, the honking huge thing which doubles as a gaming
platform, but out on the road my little laptop does these&amp;nbsp;duties.&lt;/p&gt;
&lt;p&gt;When I call my laptop &amp;#8220;little&amp;#8221;, I&amp;#8217;m not kidding. I like my diminutive, and
above all &lt;em&gt;cheap&lt;/em&gt;, computers. I really &lt;em&gt;don&amp;#8217;t&lt;/em&gt; like lugging around a thousand
quid&amp;#8217;s worth of easily breakable or nickable gear. So my current laptop is an
&lt;a class="reference external" href="https://www.acer.com/gb-en/laptops/travelmate/travelmate-spin-b1"&gt;Acer Travelmate Spin B1&lt;/a&gt; which has these positively mind-blowing&amp;nbsp;specs:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="13%" /&gt;
&lt;col width="87%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Component&lt;/th&gt;
&lt;th class="head"&gt;Specification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;CPU&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;Intel Pentium N4200 (ooooh!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Graphics&lt;/td&gt;
&lt;td&gt;Integrated Intel Graphics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span class="caps"&gt;4GB&lt;/span&gt; (yes, my Pi has more)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;&lt;span class="caps"&gt;64GB&lt;/span&gt; eMMC (again, the Pi has a bigger, faster &lt;span class="caps"&gt;SSD&lt;/span&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Display&lt;/td&gt;
&lt;td&gt;11.6&amp;#8221; 1080p (you guessed it, my Pi has a bigger monitor)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Yet, for all its diminutive capacity, it still breezes past my main Pi 4 at
boot time, and is an eminently more capable desktop machine &lt;a class="footnote-reference" href="#windows" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. In
fact, I run two Pi 4s for my work; one runs the Ubuntu desktop and the other a
customized Ubuntu server image with &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Kmscon"&gt;kmscon&lt;/a&gt; as the console. The &lt;em&gt;vast&lt;/em&gt;
majority of my work (including all email) is done on the latter because the
console is so much faster than the&amp;nbsp;desktop.&lt;/p&gt;
&lt;p&gt;So … enough waffling and delay. &lt;strong&gt;What about the Pi&amp;nbsp;5?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It beats the laptop. Hands down. I know my laptop is nothing to write home
about, but this Pi is, being the first Pi to roundly beat one of my PCs at
being a &amp;#8220;good desktop&amp;#8221;. It boots faster (about 20 seconds from cold for Ubuntu;
we&amp;#8217;re not at the blazing 7 seconds that RaspiOS manages, but Ubuntu&amp;#8217;s a rather
beefier desktop environment). It runs faster (everything launches faster, and
generally &amp;#8220;feels&amp;#8221; faster than on the laptop). It&amp;#8217;s considerably more expandable
(remember the storage on my laptop is a soldered-on eMMC module!). And yet,
it&amp;#8217;s&amp;nbsp;cheaper.&lt;/p&gt;
&lt;p&gt;The &amp;#8220;cheaper&amp;#8221; comparison obviously sounds unfair; the laptop includes a
keyboard, touchpad, battery, screen and so on, and moreover it&amp;#8217;s older
technology (newer technology is typically cheaper per unit of performance than
older). When I bought it refurbished several years ago, it still cost about 300
quid, so let&amp;#8217;s tot up my Raspberry Pi&amp;nbsp;equivalent:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The Pi 5 I&amp;#8217;ve been testing on is the &lt;span class="caps"&gt;8GB&lt;/span&gt; model (kindly provided a couple of
months ago by the folk at Raspberry Pi), which retails for 80&amp;nbsp;quid&lt;/li&gt;
&lt;li&gt;Add in a good power supply (12&amp;nbsp;quid)&lt;/li&gt;
&lt;li&gt;Reasonable keyboard (20 quid &lt;a class="footnote-reference" href="#keyboards" id="footnote-reference-2"&gt;[2]&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Mouse (10&amp;nbsp;quid)&lt;/li&gt;
&lt;li&gt;Monitor (80 quid for a 24&amp;#8221; &lt;span class="caps"&gt;FHD&lt;/span&gt;)&lt;/li&gt;
&lt;li&gt;Good &lt;span class="caps"&gt;SD&lt;/span&gt; card (15 quid for a &lt;span class="caps"&gt;128GB&lt;/span&gt;&amp;nbsp;SanDisk)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;217 of your pounds sterling. Yes, definitely &amp;#8220;cheaper&amp;#8221;, and recall we&amp;#8217;re
comparing to a &lt;em&gt;refurbished&lt;/em&gt; laptop, not a new one. It&amp;#8217;s not as portable as the
laptop, but 83 quid probably buys a fair sized power-bank! Alright, that&amp;#8217;s
still not a &lt;em&gt;fair&lt;/em&gt; comparison but I&amp;#8217;m going to make it anyway because my Pis
really do accompany me on the road&amp;nbsp;:-)&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="An image of Dave's hotel room at an engineering sprint. Amidst a collection of socks, a Pi 400 is hooked up to the huge 40-something inch TV in front of the bed. The screen is displaying the Ubuntu logo typically seen during desktop boot. In the left foreground, two toolboxes are open on the bed. On the right, a small table is covered in the detritus of the day: Dave's laptop, empty coffee mug, glasses, a copy of Private Eye, a graphic novel, and several random bits of Pi paraphenalia." src="https://waldorf.waveform.org.uk/images/hotel-4k.jpg" /&gt;
&lt;p class="caption"&gt;Dave&amp;#8217;s &amp;#8220;portable&amp;#8221;&amp;nbsp;setup.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="abracadabra"&gt;
&lt;h2&gt;Abracadabra!&lt;/h2&gt;
&lt;p&gt;What should you do to get the most out of Ubuntu on your Pi 5? Honestly, not a
great deal. Anyone who&amp;#8217;s used Ubuntu desktop on the Pi 4 extensively will be
yelling things like &amp;#8220;active cooling!&amp;#8221; and &amp;#8220;&lt;span class="caps"&gt;SSD&lt;/span&gt; boot for performance!&amp;#8221; at the
screen, but frankly these are optional on the 5 and absolutely unnecessary to
have a system that works&amp;nbsp;&amp;#8220;well&amp;#8221;.&lt;/p&gt;
&lt;p&gt;The pre-release Pi 5 sent to me a few months back (thanks Gordon!) &lt;em&gt;didn&amp;#8217;t&lt;/em&gt;
include the fancy new cooling fan &lt;a class="footnote-reference" href="#cooling" id="footnote-reference-3"&gt;[3]&lt;/a&gt;. The board does get warm,
particularly under load, and it&amp;#8217;s not too difficult to get it to throttle
(watching a video fullscreen in Firefox will do the job). But do I &lt;em&gt;notice&lt;/em&gt; it
throttling? No! And once the video finishes (or the package finishes building,
or what have you) and I go back to doing less computationally intensive things,
the board&amp;#8217;s back to an unthrottled temperature within a couple of seconds …
without any active&amp;nbsp;cooling.&lt;/p&gt;
&lt;p&gt;The other thing the Pi 5 didn&amp;#8217;t arrive with was the official power supply
&lt;a class="footnote-reference" href="#pico-probe-1" id="footnote-reference-4"&gt;[4]&lt;/a&gt;. This is a rather more important piece as, in order to reach its
full potential, the Pi 5 does have rather higher power demands. Specifically,
if you want the &lt;abbr title="Universal Serial Bus"&gt;&lt;span class="caps"&gt;USB&lt;/span&gt;&lt;/abbr&gt; ports to deliver enough
juice to run external storage (and thus to boot from an &lt;span class="caps"&gt;SSD&lt;/span&gt; drive), you need a
supply capable of delivering 5 amps at 5 volts. The &lt;em&gt;vast&lt;/em&gt; majority of
&lt;abbr title="USB Power Delivery"&gt;&lt;span class="caps"&gt;USB&lt;/span&gt;-&lt;span class="caps"&gt;PD&lt;/span&gt;&lt;/abbr&gt; compatible supplies don&amp;#8217;t manage this
(certainly none of the ones I owned could). There are a few third-party power
supplies that do (for example, the &lt;a class="reference external" href="https://docs.radxa.com/en/accessories/pd_30w"&gt;Radxa Power &lt;span class="caps"&gt;PD&lt;/span&gt; 30W&lt;/a&gt;) but my recommendation
to most would be to simply buy a &lt;a class="reference external" href="https://shop.pimoroni.com/products/raspberry-pi-27w-usb-c-power-supply"&gt;Raspberry Pi supply&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll write a bit more about this in a future article but an important aspect
here is that the official supply has a &amp;#8220;captive cable&amp;#8221;. This means that at the
&amp;#8220;business end&amp;#8221; (that sticks into your Pi), it &lt;em&gt;will&lt;/em&gt; deliver 5V. If your supply
requires an external cable, it&amp;#8217;ll presumably deliver its rated 5V at its
socket, but that doesn&amp;#8217;t guarantee it&amp;#8217;ll still be 5V at the business end of the
cable you&amp;#8217;re inserting. Furthermore, to carry 5A, &lt;span class="caps"&gt;USB&lt;/span&gt; cables need an &lt;a class="reference external" href="https://en.wikipedia.org/wiki/USB-C#Cables"&gt;e-marker
chip&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, suffice it to say, I&amp;#8217;m still running my 5 off my old 3A capable supply
(an official power supply is on order!), and even though it may not power
anything beefy off the &lt;span class="caps"&gt;USB&lt;/span&gt; ports, it&amp;#8217;s still managed everything else without
any&amp;nbsp;brown-outs.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="A Raspberry Pi 5 sits on top of a blue-painted desk. Various cables (USB, ethernet, micro-HDMI, and USB-C power cable) are plugged into it, as well as a brown ribbon cable (FFC) leading to a Raspberry Pi camera module. To the left, various jumper leads trail from the GPIO header to a small breadboard containing a rotary encoder with an RGBLED embedded within it." src="https://waldorf.waveform.org.uk/images/pi5-desk.jpg" /&gt;
&lt;p class="caption"&gt;Cool as a very odd&amp;nbsp;cucumber.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;What about storage? Much the same applies. Yes, I run my Pi 4 on an &lt;span class="caps"&gt;SSD&lt;/span&gt;, and
for doing serious work on the Pi 5 I would do exactly the same. But whereas on
the Pi 4 this was about gaining capacity &lt;em&gt;and&lt;/em&gt; performance, on the Pi 5 it&amp;#8217;s
really just down to extra capacity (and possibly reliability). The &lt;span class="caps"&gt;SD&lt;/span&gt; card
interface is so much faster on the 5 (and the &lt;span class="caps"&gt;IO&lt;/span&gt; bandwidth to other components
like memory so much bigger), that there really isn&amp;#8217;t a hard requirement to have
an &lt;span class="caps"&gt;SSD&lt;/span&gt; for good desktop performance. A &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/dealing-the-sd-cards.html"&gt;decent &lt;span class="caps"&gt;SD&lt;/span&gt; card&lt;/a&gt; is perfectly&amp;nbsp;sufficient.&lt;/p&gt;
&lt;p&gt;In summary, a throttled, under-powered, &lt;span class="caps"&gt;SD&lt;/span&gt;-card driven Pi 5 is still a
considerably more comfortable and responsive machine than an unthrottled,
fully-powered, &lt;span class="caps"&gt;SSD&lt;/span&gt;-booted Pi 4. And it&amp;#8217;s still capable of beating my&amp;nbsp;laptop!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ala-peanut-butter-sandwiches"&gt;
&lt;h2&gt;Ala Peanut Butter&amp;nbsp;Sandwiches?&lt;/h2&gt;
&lt;p&gt;So … there&amp;#8217;s &lt;em&gt;nothing&lt;/em&gt; to do? No hardware to add, no software to tinker with to
get the most out of Ubuntu on your shiny new Pi? Perish the thought! Where
would we be without the need of a bit of tinkering? &lt;a class="footnote-reference" href="#rhetorical" id="footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s a few major things that we couldn&amp;#8217;t get into the release in time. The
first is the upstream patches for libcamera (this is the reason for the camera
module plugged into the board in the shot above &amp;#8212; still testing!). This is
intended to be delivered as an &lt;abbr title="Stable Release Update"&gt;&lt;span class="caps"&gt;SRU&lt;/span&gt;&lt;/abbr&gt; in Mantic,
which isn&amp;#8217;t too bad given that libcamera isn&amp;#8217;t included in the base image so
users need to download it&amp;nbsp;anyway.&lt;/p&gt;
&lt;p&gt;The second is the mesa patches for full graphical performance. These &lt;em&gt;did&lt;/em&gt; make
it into the base image, so &lt;em&gt;most&lt;/em&gt; applications on the desktop do indeed get the
full benefit of the new Vulkan-compliant drivers. But some desktop applications
(and one rather notable one in particular) don&amp;#8217;t use the system&amp;#8217;s mesa drivers
because they&amp;#8217;re shipped as snaps, so they use the libraries included in the
corresponding content or base snaps (most of which are still from the 22.04&amp;nbsp;era).&lt;/p&gt;
&lt;p&gt;Obviously this includes&amp;nbsp;Firefox.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="In the foreground, the Pi 5 setup from the prior image can be seen (breadboard on the left, Pi 5 in the middle, camera module on the right). Behind the breadboard are some colourful lego bricks with some jumper leads trailing out of them, and a soldering iron barely visible behind them. To the right is the edge of a keyboard, and behind it a set of small drawers containing electronic components. To the rear of the desk can be seen a speaker, a monitor showing Firefox, and the background selections of Ubuntu Mantic. But not much else can be seen because of the black and white cat standing indignantly in the middle of the desk, blocking the view." src="https://waldorf.waveform.org.uk/images/pi5-cat.jpg" /&gt;
&lt;p class="caption"&gt;He&amp;#8217;s not&amp;nbsp;helping.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For the most part, this doesn&amp;#8217;t matter too much. Browsing the web and even
playing video is still fine because the &lt;span class="caps"&gt;CPU&lt;/span&gt;&amp;#8217;s capable of just powering through
(the Pi 5 really does have a beast of a processor!). However, fire up the
&lt;a class="reference external" href="https://webglsamples.org/aquarium/aquarium.html"&gt;WebGL aquarium&lt;/a&gt; sample and it becomes blindingly obvious. It&amp;#8217;ll barely manage
a paltry 2fps, if you&amp;#8217;re lucky. Replace the Firefox snap with a deb so it&amp;#8217;s
using the system mesa libraries (I won&amp;#8217;t go over the instructions again here,
but you can derive them from &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html#adding-sprinkles"&gt;this post&lt;/a&gt; if you really want to)
and all of a sudden that jumps up to a much more respectable 40fps or so (at
least on my 1080p&amp;nbsp;monitor).&lt;/p&gt;
&lt;p&gt;Naturally, the plan is to include the updated mesa drivers in the base snaps as
soon as we&amp;#8217;re able after launch, so hopefully this should be a short-lived
state of affairs. Also, as noted, this doesn&amp;#8217;t appear to affect the vast
majority of web browsing, only rather niche things like&amp;nbsp;WebGL.&lt;/p&gt;
&lt;p&gt;The other missing bit is a power monitoring daemon, which is something I&amp;#8217;ll be
prioritising in the next cycle. The new power management chip on the Pi 5
allows the &lt;span class="caps"&gt;OS&lt;/span&gt; to determine whether the previous reboot was due to a power
brownout. This is fantastic as brownout issues are notoriously variable and
difficult to diagnose, so having the hardware tell you that this was definitely
the cause, should deal with a whole pile of&amp;nbsp;Heisenbugs.&lt;/p&gt;
&lt;p&gt;Finally, the other major change with the Pi 5 is the means by which
&lt;abbr title="General Purpose Input Output"&gt;&lt;span class="caps"&gt;GPIO&lt;/span&gt;&lt;/abbr&gt; pins are controlled. No longer is
the &lt;abbr title="System on a Chip"&gt;SoC&lt;/abbr&gt; itself driving the GPIOs. The new &lt;a class="reference external" href="https://www.raspberrypi.com/news/rp1-the-silicon-controlling-raspberry-pi-5-i-o-designed-here-at-raspberry-pi/"&gt;&lt;span class="caps"&gt;RP1&lt;/span&gt;
&amp;#8220;southbridge&amp;#8221;&lt;/a&gt; is now in charge instead. This means that the traditional &lt;span class="caps"&gt;GPIO&lt;/span&gt;
libraries like RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; no longer work. I&amp;#8217;ve written about this &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2022/the-one-where-dave-breaks-stuff.html"&gt;previously&lt;/a&gt; and thankfully Ubuntu&amp;#8217;s pretty well placed
to weather the storm here. We switched the &lt;span class="caps"&gt;GPIO&lt;/span&gt; Zero back-end over back in
hirsute (21.04) and added &lt;a class="reference external" href="https://rpi-lgpio.readthedocs.io/"&gt;rpi-lgpio&lt;/a&gt; (a RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; compatible shim) back in
kinetic (22.10) to ease the&amp;nbsp;transition.&lt;/p&gt;
&lt;p&gt;These pieces needed a few updates &lt;a class="footnote-reference" href="#pinout" id="footnote-reference-6"&gt;[6]&lt;/a&gt; to deal with the Pi 5&amp;#8217;s new
hardware, but that&amp;#8217;s all included in mantic. With a bit of luck most users
either won&amp;#8217;t notice the change, or won&amp;#8217;t have &lt;em&gt;much&lt;/em&gt; difficulty in getting
their circuits to work on their shiny new Pi&amp;nbsp;5.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s all for now. Go grab your copy of Mantic Minotaur (it should be in
&lt;a class="reference external" href="https://www.raspberrypi.com/software/"&gt;rpi-imager&lt;/a&gt; by the time I publish this) and have&amp;nbsp;fun!&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="windows" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Running Windows on it was as awful as you might imagine, but
Ubuntu is still light enough to be perfectly useable on it&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="keyboards" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;At this point, anybody who knows me is spluttering their tea
over the screen; &amp;#8220;twenty quid? Sure you&amp;#8217;re not missing a zero there, Dave?&amp;#8221;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="cooling" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;There weren&amp;#8217;t enough to give one to each alpha tester at that
point, and it wasn&amp;#8217;t necessary for Ubuntu&amp;#8217;s compatibility work&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="pico-probe-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Again, there weren&amp;#8217;t enough to go around. However, I should
add that they did very kindly (and usefully!) ship a &lt;a class="reference external" href="https://www.raspberrypi.com/products/debug-probe/"&gt;Pico Probe&lt;/a&gt; with the
board which was &lt;em&gt;really&lt;/em&gt; useful in debugging things, especially since it
attaches to the separate debug port now, meaning I didn&amp;#8217;t have to unplug
things from the &lt;span class="caps"&gt;GPIO&lt;/span&gt; pins just to get a serial console&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="rhetorical" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;That&amp;#8217;s rhetorical :)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="pinout" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Try &lt;tt class="docutils literal"&gt;pinout&lt;/tt&gt; on the new Pi 5!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="desktop"></category><category term="pi"></category><category term="mantic"></category></entry><entry><title>Dealing the (SD) Cards</title><link href="https://waldorf.waveform.org.uk/2023/dealing-the-sd-cards.html" rel="alternate"></link><published>2023-09-12T00:00:00+01:00</published><updated>2025-02-27T23:04:13+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-09-12:/2023/dealing-the-sd-cards.html</id><summary type="html">&lt;p class="first last"&gt;A brief guide to finding reliable &lt;span class="caps"&gt;SD&lt;/span&gt; cards for the&amp;nbsp;Pi&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;Updated&lt;/strong&gt; to remove large outlet recommendations, and fix&amp;nbsp;footnote&lt;/p&gt;
&lt;p&gt;It came to my attention recently that many people don&amp;#8217;t quite know what to look
for in a micro-&lt;span class="caps"&gt;SD&lt;/span&gt; card for use in a Pi. As mercilessly torturing micro-&lt;span class="caps"&gt;SD&lt;/span&gt; cards
is something of a hobby round here I thought I&amp;#8217;d write a quick guide to what to
look&amp;nbsp;for.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="A variety of micro-SD cards arranged in an arc, adorned by various logos (SanDisk, Samsung, Transcend, Team), in a variety of capacities from the tiny (512MB) up to the moderately large (64GB). In a smaller arc below are a selection of &amp;quot;full-sized&amp;quot; SD card adapters, one of which bears the Raspberry Pi logo." src="https://waldorf.waveform.org.uk/images/sdcards2.webp" /&gt;
&lt;p class="caption"&gt;A small selection of the &lt;span class="caps"&gt;SD&lt;/span&gt; cards that have graced my desk over the years,
ranging from the rubbish to the really rather&amp;nbsp;good.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="vegas-standard"&gt;
&lt;h2&gt;Vegas&amp;nbsp;Standard&lt;/h2&gt;
&lt;p&gt;There have been many standards for &lt;span class="caps"&gt;SD&lt;/span&gt; cards to conform to over the years, but
the vast majority of them are concerned with &lt;em&gt;sequential&lt;/em&gt; transfer rates. These
started with the so-called &lt;a class="reference external" href="https://en.wikipedia.org/wiki/SD_card#Class"&gt;speed classes&lt;/a&gt;, where Class 4 implied that a card
could sustain a write speed of &lt;span class="caps"&gt;4MB&lt;/span&gt;/sec to the card, Class 6 implied &lt;span class="caps"&gt;6MB&lt;/span&gt;/sec and
so&amp;nbsp;on.&lt;/p&gt;
&lt;p&gt;Later there were the &lt;abbr title="Ultra High Speed"&gt;&lt;span class="caps"&gt;UHS&lt;/span&gt;&lt;/abbr&gt; speed classes, where U1 was
equivalent to the older Class-10, a sustained write speed of &lt;span class="caps"&gt;10MB&lt;/span&gt;/sec to the
card. U3 implied &lt;span class="caps"&gt;30MB&lt;/span&gt;/s sustained write speed. Why a new class system? Because
&lt;span class="caps"&gt;UHS&lt;/span&gt; also required a specific bus speed (confusingly designated &lt;span class="caps"&gt;UHS&lt;/span&gt;-I, then
&lt;span class="caps"&gt;UHS&lt;/span&gt;-&lt;span class="caps"&gt;II&lt;/span&gt;, etc). Hence, a U1 card must manage &lt;span class="caps"&gt;10MB&lt;/span&gt;/sec &lt;em&gt;minimum&lt;/em&gt; but the
corresponding bus speed could top out at &lt;span class="caps"&gt;104MB&lt;/span&gt;/s allowing considerably faster
read speeds (depending on bus&amp;nbsp;compatibility).&lt;/p&gt;
&lt;p&gt;Later still came the video speed classes. These still use the &lt;span class="caps"&gt;UHS&lt;/span&gt; bus
interfaces, but demand higher minimum sequential write speeds. V10 requires
&lt;span class="caps"&gt;10MB&lt;/span&gt;/s minimum, V60 requires &lt;span class="caps"&gt;60MB&lt;/span&gt;/s minimum and so on. Here&amp;#8217;s a nice little
table outlining the minimums required by each of these&amp;nbsp;classes.&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="37%" /&gt;
&lt;col width="16%" /&gt;
&lt;col width="22%" /&gt;
&lt;col width="25%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Minimum write performance&lt;/th&gt;
&lt;th class="head"&gt;Speed Class&lt;/th&gt;
&lt;th class="head"&gt;&lt;span class="caps"&gt;UHS&lt;/span&gt; Speed Class&lt;/th&gt;
&lt;th class="head"&gt;Video Speed Class&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;2MB&lt;/span&gt;/s&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-class-2.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; Class 2 logo, a &amp;#8220;2&amp;#8221; within a stylized &amp;#8220;C&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;td&gt;&amp;#8212;&lt;/td&gt;
&lt;td&gt;&amp;#8212;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;4MB&lt;/span&gt;/s&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-class-4.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; Class 4 logo, a &amp;#8220;4&amp;#8221; within a stylized &amp;#8220;C&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;td&gt;&amp;#8212;&lt;/td&gt;
&lt;td&gt;&amp;#8212;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;6MB&lt;/span&gt;/s&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-class-6.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; Class 6 logo, a &amp;#8220;6&amp;#8221; within a stylized &amp;#8220;C&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;td&gt;&amp;#8212;&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-video-6.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; video 6 logo, a stylized &amp;#8220;V&amp;#8221; almost like a square root symbol
followed by a &amp;#8220;6&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;10MB&lt;/span&gt;/s&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-class-10.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; Class 10 logo, a &amp;#8220;10&amp;#8221; within a stylized &amp;#8220;C&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-UHS-1.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; &lt;span class="caps"&gt;UHS&lt;/span&gt; 1 logo, a &amp;#8220;1&amp;#8221; within a stylized &amp;#8220;U&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-video-10.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; video 10 logo, a stylized &amp;#8220;V&amp;#8221; almost like a square root symbol
followed by a &amp;#8220;10&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;30MB&lt;/span&gt;/s&lt;/td&gt;
&lt;td&gt;〃&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-UHS-3.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; &lt;span class="caps"&gt;UHS&lt;/span&gt; 3 logo, a &amp;#8220;3&amp;#8221; within a stylized &amp;#8220;U&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-video-30.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; video 30 logo, a stylized &amp;#8220;V&amp;#8221; almost like a square root symbol
followed by a &amp;#8220;30&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;60MB&lt;/span&gt;/s&lt;/td&gt;
&lt;td&gt;〃&lt;/td&gt;
&lt;td&gt;〃&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-video-60.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; video 60 logo, a stylized &amp;#8220;V&amp;#8221; almost like a square root symbol
followed by a &amp;#8220;60&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span class="caps"&gt;90MB&lt;/span&gt;/s&lt;/td&gt;
&lt;td&gt;〃&lt;/td&gt;
&lt;td&gt;〃&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-video-90.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; video 90 logo, a stylized &amp;#8220;V&amp;#8221; almost like a square root symbol
followed by a &amp;#8220;90&amp;#8221;&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Got all that? Good. Now you can forget&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;As I mentioned above all of this is to do with &lt;em&gt;sequential&lt;/em&gt; access. This is
very useful in a camera where you&amp;#8217;re recording video frame after video frame
(or still image cameras that do … erm … exactly the same thing only much slower
with considerably larger frames!). However, this doesn&amp;#8217;t remotely resemble the
access pattern of an operating system on an &lt;span class="caps"&gt;SD&lt;/span&gt; card which will typically entail
hundreds of small writes to small files spread across a multitude of&amp;nbsp;sectors.&lt;/p&gt;
&lt;p&gt;In other words these classes will tell you next to &lt;em&gt;nothing&lt;/em&gt; about the
performance of your card in a Raspberry Pi. For that you need guarantees about
the minimum &lt;em&gt;random&lt;/em&gt; access performance. Luckily, the recent &lt;span class="caps"&gt;SD&lt;/span&gt; specifications
have a standard for that too: the application performance&amp;nbsp;classes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="poker-face"&gt;
&lt;h2&gt;Poker&amp;nbsp;Face&lt;/h2&gt;
&lt;p&gt;At the time of writing, two application classes are defined: A1 which is
fairly common on decent cards, and A2 which is still somewhat &amp;#8220;premium&amp;#8221;. Their
minimum guarantees are as&amp;nbsp;follows:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="36%" /&gt;
&lt;col width="38%" /&gt;
&lt;col width="26%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Minimum read performance&lt;/th&gt;
&lt;th class="head"&gt;Minimum write performance&lt;/th&gt;
&lt;th class="head"&gt;Application class&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;1500 &lt;span class="caps"&gt;IOPS&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;500 &lt;span class="caps"&gt;IOPS&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-app-1.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; video application-performance logo, a stylized &amp;#8220;A&amp;#8221; followed by
a &amp;#8220;1&amp;#8221; with &amp;#8220;app performance&amp;#8221; in capitals below&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4000 &lt;span class="caps"&gt;IOPS&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;2000 &lt;span class="caps"&gt;IOPS&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;object data="https://waldorf.waveform.org.uk/images/SD-app-2.svg" style="height: 1em;" type="image/svg+xml"&gt;The &lt;span class="caps"&gt;SD&lt;/span&gt; video application-performance logo, a stylized &amp;#8220;A&amp;#8221; followed by
a &amp;#8220;2&amp;#8221; with &amp;#8220;app performance&amp;#8221; in capitals below&lt;/object&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Note that these guarantee both write &lt;em&gt;and read&lt;/em&gt; minimums. Further, they&amp;#8217;re not
concerned with bytes transferred, but with &lt;a class="reference external" href="https://en.wikipedia.org/wiki/IOPS"&gt;&lt;span class="caps"&gt;IOPS&lt;/span&gt;&lt;/a&gt;, or I/O operations per
second. The exact meaning of &lt;span class="caps"&gt;IOPS&lt;/span&gt; is rather loose across storage generally but
here it means the number of block sized (&lt;span class="caps"&gt;4KB&lt;/span&gt;) read or write commands that can
be carried out per second. Even this is relatively fuzzy because the controller
in an &lt;span class="caps"&gt;SD&lt;/span&gt; card is free to batch operations together. Erasure is almost always
batched into &amp;#8220;extents&amp;#8221; of blocks, and writes can be generally&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;In addition to the above requirements, A1 and A2 both require a minimum of
&lt;span class="caps"&gt;10MB&lt;/span&gt;/s sequential write speed (equivalent to the old Class 10 cards). This may
&lt;em&gt;appear&lt;/em&gt; excessive. After all, if A1 only demands that a card handle 500x &lt;span class="caps"&gt;4KB&lt;/span&gt;
writes per second, that only equates to &lt;span class="caps"&gt;2MB&lt;/span&gt;/s? However, remember that cards are
free to batch operations together. Cards that do &lt;em&gt;not&lt;/em&gt; conform to one of the
application classes may well display &lt;span class="caps"&gt;10MB&lt;/span&gt;/s sequential writes to one large file
by batching together smaller writes that are all in sequence. However, if asked
to perform many small &lt;span class="caps"&gt;4KB&lt;/span&gt;/s writes to a multitude of blocks, the performance
suddenly drops off a&amp;nbsp;cliff.&lt;/p&gt;
&lt;p&gt;In my experience, any reputable card with an A1 mark performs &amp;#8220;well&amp;#8221; on a Pi.
A2 cards from a given vendor perform better than A1 cards from that same
vendor, but I&amp;#8217;ve also had A1 cards from vendor A beat A2 cards from vendor B.
But when I say &amp;#8220;beats&amp;#8221;, I mean in performance benchmarks. I cannot say I ever
noticed a difference in daily&amp;nbsp;usage.&lt;/p&gt;
&lt;p&gt;To put it another way: A1 is the sweet spot right now, and A2 is &lt;em&gt;probably&lt;/em&gt; a
waste of money at this point unless you&amp;#8217;re quite sure you absolutely need the&amp;nbsp;performance.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="shooting-craps"&gt;
&lt;h2&gt;Shooting&amp;nbsp;Craps&lt;/h2&gt;
&lt;p&gt;The astute reader may have noticed my careful use of the word &amp;#8220;reputable&amp;#8221; just
before &amp;#8220;card&amp;#8221; in the prior section. &lt;a class="reference external" href="https://www.bunniestudios.com/blog/?p=918"&gt;Fakes&lt;/a&gt; are a problem, or have been in my
experience (though we&amp;#8217;re going back several years&amp;nbsp;here).&lt;/p&gt;
&lt;p&gt;Buy from well-known outlets, not minor vendors on whatever variant of
flea-bay you frequent &lt;a class="footnote-reference" href="#fleabay" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. Just because it&amp;#8217;s on Amazon, doesn&amp;#8217;t mean
it&amp;#8217;s not a knock-off; &lt;span class="strike"&gt;check the actual seller&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Cheap, bulk deals are especially to be avoided. There&amp;#8217;s enough competition in
the micro-&lt;span class="caps"&gt;SD&lt;/span&gt; card market that there&amp;#8217;s usually little that can be (legitimately)
saved by buying in bulk; if the unit price is substantially below what you
could get the individual cards for, be&amp;nbsp;wary!&lt;/p&gt;
&lt;p&gt;To&amp;nbsp;summarize:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Buy well-known&amp;nbsp;brands,&lt;/li&gt;
&lt;li&gt;from reputable&amp;nbsp;vendors,&lt;/li&gt;
&lt;li&gt;avoiding bulk (or suspiciously cheap)&amp;nbsp;deals.&lt;/li&gt;
&lt;li&gt;Look for A1 or A2 on the casing &lt;a class="footnote-reference" href="#formula1" id="footnote-reference-2"&gt;[3]&lt;/a&gt;. Any modern card &lt;em&gt;without&lt;/em&gt; those
marks is likely to be rubbish for use in an &lt;abbr title="Single Board Computer"&gt;&lt;span class="caps"&gt;SBC&lt;/span&gt;&lt;/abbr&gt; like a Raspberry&amp;nbsp;Pi.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="grid col-2 docutils container"&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/SD-app-1.svg" type="image/svg+xml"&gt;&lt;/object&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/SD-app-2.svg" type="image/svg+xml"&gt;&lt;/object&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="the-long-game"&gt;
&lt;h2&gt;The Long&amp;nbsp;Game&lt;/h2&gt;
&lt;p&gt;I &lt;em&gt;haven&amp;#8217;t&lt;/em&gt; had any issues for the last few years, but during them I&amp;#8217;ve stuck
to buying strictly from either SanDisk or Samsung (also had a Toshiba card that
lasted many years without issue, but it&amp;#8217;s a singleton sample), and avoiding any
cheap-looking bulk&amp;nbsp;deals.&lt;/p&gt;
&lt;p&gt;This is not to say these cards last forever. But I have several Pis that run
24x7, and &amp;#8220;good&amp;#8221; cards seem quite capable of lasting at least 5 years in them,
which is &amp;#8220;good enough&amp;#8221; for my purposes (I&amp;#8217;ve had hard drives last less than
that&amp;nbsp;before!).&lt;/p&gt;
&lt;p&gt;The failure modes of micro-&lt;span class="caps"&gt;SD&lt;/span&gt; cards seem to fall into two&amp;nbsp;camps:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;completely dead &amp;#8212; the card can be detected in a reader but fails to mount
at&amp;nbsp;all&lt;/li&gt;
&lt;li&gt;going &amp;#8220;read-only&amp;#8221; &amp;#8212; the card will happily mount, and read pretty much
anything but all writes are silently thrown&amp;nbsp;away&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The latter &lt;em&gt;sounds&lt;/em&gt; preferable, and in some ways it is because if you have any
data you want to recover from it, you can do so. But it&amp;#8217;s also worse in certain&amp;nbsp;circumstances.&lt;/p&gt;
&lt;p&gt;Consider a board where the card &amp;#8220;goes read-only&amp;#8221;. It may appear to continue
operating quite happily (if it&amp;#8217;s serving a static site for instance), but all
writes to logs are being thrown away. It&amp;#8217;s also quite confusing to diagnose if
you&amp;#8217;re not specifically looking for it because the card may &lt;em&gt;appear&lt;/em&gt; to be
operating&amp;nbsp;normally.&lt;/p&gt;
&lt;p&gt;When dealing with a card I know to be old one of the first things I&amp;#8217;ll try
(assuming I can mount it at all), is &lt;tt class="docutils literal"&gt;touch foo&lt;/tt&gt;, remove and re-mount the
card, followed by &lt;tt class="docutils literal"&gt;ls&lt;/tt&gt; just to see if an empty &amp;#8220;foo&amp;#8221; file shows up. If it
doesn&amp;#8217;t, the card&amp;#8217;s almost certainly gone read-only and it&amp;#8217;s time to make a
fresh&amp;nbsp;one.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="fleabay" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;eBay, Amazon, Alibaba, whoever. If it&amp;#8217;s a large marketplace, I&amp;#8217;ve
had fakes from it (certainly from the aforementioned big three). Ideally,
buy from your local electronics vendor (&lt;a class="reference external" href="https://shop.pimoroni.com/products/microsd-card-with-raspberry-pi-os?variant=31703694245971"&gt;Pimoroni&lt;/a&gt; &lt;a class="footnote-reference" href="#pimcard" id="footnote-reference-3"&gt;[2]&lt;/a&gt; or &lt;a class="reference external" href="https://thepihut.com/products/sandisk-microsd-card-class-10-a1"&gt;The Pi
Hut&lt;/a&gt; for me) &lt;span class="strike"&gt;but failing that, look for the larger vendors on the
big marketplaces&lt;/span&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="pimcard" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="strike"&gt;Unfortunately, at the time of writing, Pimoroni&amp;#8217;s &lt;span class="caps"&gt;SD&lt;/span&gt;
card offering doesn&amp;#8217;t include shots of the *front* of the card. Being a
reputable company, I&amp;#8217;d be extremely surprised if they weren&amp;#8217;t good quality
A1 cards, but it&amp;#8217;d be nice to see this at least suggested visually!&lt;/span&gt; Oooh,
they&amp;#8217;re A2 cards!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="formula1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Assuming you can make it out from the cacophany of logos. Is it
just me, or are micro-&lt;span class="caps"&gt;SD&lt;/span&gt; card cases beginning to look like the side of a
Formula 1 car?&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="sd"></category><category term="reliability"></category></entry><entry><title>The Ubuntu Desktop</title><link href="https://waldorf.waveform.org.uk/2023/the-ubuntu-desktop.html" rel="alternate"></link><published>2023-08-09T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-08-09:/2023/the-ubuntu-desktop.html</id><summary type="html">&lt;p class="first last"&gt;Comparing the official desktop with the cloud-init baked&amp;nbsp;flavours&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: The &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; which links to all the other flavour&amp;nbsp;posts.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#8217;ll be comparing the official Ubuntu desktop release with all
the flavours we&amp;#8217;ve been baking from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configurations, on a
Raspberry&amp;nbsp;Pi.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#ubuntu" id="toc-entry-1"&gt;Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#sort-it-out" id="toc-entry-2"&gt;Sort it&amp;nbsp;out!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#popping-and-locking" id="toc-entry-3"&gt;Popping and&amp;nbsp;locking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#verdict" id="toc-entry-4"&gt;Verdict&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="ubuntu"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Ubuntu&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Finally we come to the comparison with the official desktop. Obviously I don&amp;#8217;t
need to include any &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; bits here because … you just install the
desktop image on your card and that&amp;#8217;s&amp;nbsp;it.&lt;/p&gt;
&lt;img alt="A screenshot of the classic Ubuntu GNOME-based desktop. The bar at the left contains (from top to bottom) the quick-launch icons (Firefox, Thunderbird, the file-browser, etc.), the icons of launched applications, and the 9-grid of the start button at the bottom. Across the top of the screen is another bar displaying &amp;quot;Activities&amp;quot; (effectively another start button), the current date and time, and finally the system tray. In the main portion of the screen can be seen Firefox show a Raspberry Pi page, part of an integrated component's PDF datasheet in evince, top running in the terminal, a file-browser in the background, and totem failing to play a video." src="https://waldorf.waveform.org.uk/images/desktops-lunar-ubuntu.png" /&gt;
&lt;p&gt;The official desktop has a few other simplicity tricks up its sleeve too: no
need to explicitly select a Wayland-based desktop, you get one by default. The
first-time setup wizard also handles configuring things like the keyboard
layout and initial user without having to deal with &lt;span class="caps"&gt;YAML&lt;/span&gt;. So … points for&amp;nbsp;simplicity?&lt;/p&gt;
&lt;p&gt;The default applications are a mix of the brilliant (the aforementioned
&lt;a class="reference external" href="https://wiki.gnome.org/Apps/Rhythmbox"&gt;Rhythmbox&lt;/a&gt;) and the … not so brilliant. In the screenshot above you can see
&lt;a class="reference external" href="https://gitlab.gnome.org/GNOME/totem"&gt;Totem&lt;/a&gt; failing to play anything (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/totem/+bug/1998782"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1998782&lt;/a&gt; is the Ubuntu bug, but the
&lt;a class="reference external" href="https://gitlab.gnome.org/GNOME/totem/-/issues/523"&gt;upstream bug&lt;/a&gt; has more
details), so it was back to &lt;tt class="docutils literal"&gt;sudo apt install vlc&lt;/tt&gt;. The incorrect audio
selection bug (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/pipewire/+bug/1993347"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1993347&lt;/a&gt;) also popped up but is trivial as ever to deal&amp;nbsp;with.&lt;/p&gt;
&lt;p&gt;Everything else is mostly fine: good terminal, fine web-browser, stuff opens
happily even on unmounted network shares, though (as noted before) dragging
files from an archive to extract them doesn&amp;#8217;t work (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/file-roller/+bug/1923033"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1923033&lt;/a&gt;). Though
this is ultimately due to the Wayland transition, it&amp;#8217;s notable that it &lt;em&gt;does&lt;/em&gt;
work in Kubuntu, even when running Plasma under&amp;nbsp;Wayland.&lt;/p&gt;
&lt;p&gt;One other difference worth noting is that on this distribution I stuck with
using the snap version of Firefox because we may as well do a comparison of
stuff &amp;#8220;as it ships&amp;#8221; here (which we can&amp;#8217;t really do with the other flavours).
Despite being a snap, the Wayland support works (once the
&lt;tt class="docutils literal"&gt;MOZ_ENABLE_WAYLAND=1&lt;/tt&gt; setting is exported), and since &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/evince/+bug/1794064"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1974064&lt;/a&gt; was
fixed &lt;a class="footnote-reference" href="#redo2" id="footnote-reference-1"&gt;[1]&lt;/a&gt; opening links from a standalone &lt;span class="caps"&gt;PDF&lt;/span&gt; reader works&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;Speaking of Wayland, all the desktop animations are generally smooth and don&amp;#8217;t
lag. With the exception of the start&amp;nbsp;menu.&lt;/p&gt;
&lt;p&gt;Oh dear, it&amp;#8217;s rant time&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="sort-it-out"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Sort it&amp;nbsp;out!&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The full-screen zoom-out animation is just too large to do smoothly even under
Wayland (sometimes it manages it after a few attempts). However, this doesn&amp;#8217;t
bother me too much because, once it&amp;#8217;s deigned to appear, I can never find stuff
in it&amp;nbsp;anyway.&lt;/p&gt;
&lt;p&gt;Some flavours have a categorical start menu: Lubuntu, Xubuntu, Kubuntu, and
Studio. It&amp;#8217;s a bit dated, but it works: find the category you want, scroll
through the alphabetical list, and pick the application you want. Others take a
more smart-phone-y approach and just have a big alphabetical list of everything
installed to scroll through: Budgie, Unity … &lt;span class="caps"&gt;GNOME&lt;/span&gt;? &lt;span class="caps"&gt;GNOME&lt;/span&gt; has one big list. But
it&amp;#8217;s not&amp;nbsp;alphabetical:&lt;/p&gt;
&lt;img alt="A screenshot of the first page of GNOME's application launcher. Below two mini desktops at the top centre, is a large 6x4 grid of application icons filling the rest of the screen. The icons at first glance appear to be in alphabetical order starting with &amp;quot;Additional Drivers, &amp;quot;AisleRiot Solitaire&amp;quot;, &amp;quot;Calendar&amp;quot;, &amp;quot;GVim&amp;quot;, &amp;quot;Videos&amp;quot;, but then &amp;quot;Language Support&amp;quot;, &amp;quot;Calculator&amp;quot;, &amp;quot;LibreOffice&amp;quot;, &amp;quot;Document Scanner&amp;quot;, and so on. Two dots at the bottom indicate there are two pages of applications, and we are on the first." src="https://waldorf.waveform.org.uk/images/gnome-start-1.png" /&gt;
&lt;p&gt;It&amp;#8217;s not in most-recently-used order either. I have fired up Videos here, but
not GVim for instance. Maybe something weird like installation order? Let&amp;#8217;s
install something and see. I&amp;#8217;ve picked &lt;span class="caps"&gt;GIMP&lt;/span&gt; which, after installation, appears
at the &lt;em&gt;end&lt;/em&gt; of the list on the second screen, after all the other apparently
alphabetically arranged&amp;nbsp;items:&lt;/p&gt;
&lt;img alt="Another screenshot of GNOME's application launcher, showing the second page. This page is alphabetically ordered left-to-right, top-to-bottom, but for the final entry: the freshly installed &amp;quot;GNU Image Manipulation Program&amp;quot; which appears after &amp;quot;Vim&amp;quot;." src="https://waldorf.waveform.org.uk/images/gnome-start-2.png" /&gt;
&lt;p&gt;But, if we logout and login again, our start menu has re-arranged and now &lt;span class="caps"&gt;GIMP&lt;/span&gt;
is &lt;em&gt;before&lt;/em&gt; GVim, in apparently alphabetical order. But no! &amp;#8220;Cheese&amp;#8221; is now
before &amp;#8220;Additional Drivers&amp;#8221; although prior to the reboot it came later in the&amp;nbsp;list!&lt;/p&gt;
&lt;img alt="Back to the first page of GNOME's application launcher. Once again, a 6x4 grid of large launch icons, but in a completely different order to before. Now the list starts &amp;quot;Videos&amp;quot;, &amp;quot;Calculator&amp;quot;, &amp;quot;Document Scanner&amp;quot;, &amp;quot;Settings&amp;quot;, &amp;quot;System Monitor&amp;quot;, &amp;quot;Terminal&amp;quot;, &amp;quot;Utilities&amp;quot;, &amp;quot;Cheese&amp;quot;, &amp;quot;Additional Drivers&amp;quot;, &amp;quot;AisleRiot Solitaire&amp;quot;, &amp;quot;Calendar&amp;quot;, &amp;quot;GNU Image Manipulation Program&amp;quot;, etc." src="https://waldorf.waveform.org.uk/images/gnome-start-3.png" /&gt;
&lt;p&gt;Okay, I give up. I&amp;#8217;ve no idea what the order is here. Maybe it&amp;#8217;s &amp;#8220;whatever you
want it to be&amp;#8221;? You can re-order things manually by dragging them around, or
drag them over each other to form groups (such as the pre-grouped &amp;#8220;Utilities&amp;#8221;).
But this means that if I want a sensible layout (which the default … isn&amp;#8217;t), I
have to laboriously drag things around until they&amp;#8217;re &amp;#8220;just so&amp;#8221;. I have to put
work in (something no other flavour has demanded). Or if you just like them
alphabetical then simply … erm … &lt;a class="reference external" href="https://www.omgubuntu.co.uk/2022/01/gnome-shell-alphabetical-app-grid-extension"&gt;install an extension&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;In essence the starter is a strange mash-up of &amp;#8220;smart-phone home screen&amp;#8221; and
&amp;#8220;smart-phone all-applications list&amp;#8221;, made all the more smart-phone-y by
scrolling sideways because … well, just because. The thing is … this isn&amp;#8217;t a
bug. This is genuinely &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;#8217;s idea of &amp;#8220;how a start menu should&amp;nbsp;work&amp;#8221;!&lt;/p&gt;
&lt;p&gt;The mitigation to this is that most flavours these days, including &lt;span class="caps"&gt;GNOME&lt;/span&gt; (but
even some which favour a categorised menu like Kubuntu) allow you to &lt;em&gt;search&lt;/em&gt;
by just opening the start menu, typing a few letters of what you want, then
hitting Enter when it&amp;#8217;s the first in the list. This is how I tend to use the
start menu on &lt;span class="caps"&gt;GNOME&lt;/span&gt; because it&amp;#8217;s the only way it makes any sense. But, for this
purpose I don&amp;#8217;t &lt;em&gt;care&lt;/em&gt; about the icons, and that in turns means I don&amp;#8217;t care
about &lt;em&gt;giant full-screen zooming animations&lt;/em&gt; that lag the entire&amp;nbsp;menu.&lt;/p&gt;
&lt;p&gt;Okay, rant&amp;nbsp;over.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="popping-and-locking"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Popping and&amp;nbsp;locking&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Oh, and the screen-saver? Blanks and locks the screen, but fails to suspend the
monitor. I&amp;#8217;ve dug into this before (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/mutter/+bug/1998716"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1998716&lt;/a&gt;) and all I came away with
was a new-found appreciation for how &lt;a class="reference external" href="https://www.jwz.org/xscreensaver/toolkits.html"&gt;bloody complicated&lt;/a&gt; something as
apparently simple as a screen-saver is, once you begin to understand all that
it&amp;nbsp;does.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="verdict"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Verdict&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s not lightweight, but it is pretty, reasonably responsive, with a decent
selection of apps, and despite a few Wayland transition issues (like drag and
drop) it&amp;#8217;s mostly functional … except for that start menu. Still, it can&amp;#8217;t be
&lt;em&gt;that&lt;/em&gt; bad as it has been my main desktop for the last few&amp;nbsp;years.&lt;/p&gt;
&lt;p&gt;The problem is … now I&amp;#8217;ve used Kubuntu&amp;nbsp;…&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="redo2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;during the writing of this series in fact — this is another bit
I&amp;#8217;ve had to go back and re-write!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="lunar"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>The Ubuntu Studio</title><link href="https://waldorf.waveform.org.uk/2023/the-ubuntu-studio.html" rel="alternate"></link><published>2023-08-08T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-08-08:/2023/the-ubuntu-studio.html</id><summary type="html">&lt;p class="first last"&gt;Baking an Ubuntu Studio Lunar desktop with&amp;nbsp;cloud-init&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: The &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; which links to all the other flavour posts.
Skip &lt;a class="reference internal" href="#down-here"&gt;down here&lt;/a&gt; for the configurations&amp;nbsp;used.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#8217;ll be looking at creating an Ubuntu Studio desktop with a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, on a Raspberry&amp;nbsp;Pi.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#ubuntu-studio" id="toc-entry-1"&gt;Ubuntu&amp;nbsp;Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#verdict" id="toc-entry-2"&gt;Verdict&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#configuration" id="toc-entry-3"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="ubuntu-studio"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Ubuntu&amp;nbsp;Studio&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Originally, this was not a flavour I was planning to cover. After all Ubuntu
Studio is intended for multimedia creation which is some of the heaviest-duty
work you can put any computer through. However, I was also aware they&amp;#8217;d
recently switched to Plasma (from &lt;span class="caps"&gt;KDE&lt;/span&gt;) as a basis, and that meant Wayland was
supported. After Kubuntu&amp;#8217;s striking performance, I was intrigued to see how (or
if!) it&amp;#8217;d work on a&amp;nbsp;Pi.&lt;/p&gt;
&lt;p&gt;For Ubuntu Studio, the &lt;tt class="docutils literal"&gt;packages&lt;/tt&gt; section in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; contains an
apparent multitude of&amp;nbsp;packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-audio&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-graphics&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-photography&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-video&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-publishing&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The various &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntustudio-*&lt;/span&gt;&lt;/tt&gt; packages &lt;em&gt;other&lt;/em&gt; than &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntustudio-desktop&lt;/span&gt;&lt;/tt&gt;
are all optional depending on what you want to use. For example, if you&amp;#8217;re only
interested in audio work, leave out the graphics, photography, video, and
publishing meta-packages. I would recommend leaving out video because doing
video-editing on a Pi is … &amp;#8220;optimistic&amp;#8221; to say the least. However, one oddity
of Ubuntu Studio is that it does &amp;#8220;assume&amp;#8221; a certain set of installed packages
in the default quick launcher. If you exclude certain meta-packages you may
find a number of blank &amp;#8220;?&amp;#8221; icons in the quick launch bar because of &amp;#8220;missing&amp;#8221;
applications. You can un-pin these items without&amp;nbsp;consequence.&lt;/p&gt;
&lt;p&gt;One other note on default applications: Ubuntu Studio typically ships with
&lt;a class="reference external" href="https://freeshow.app/"&gt;FreeShow&lt;/a&gt; (which seems to be a dead link at the time of writing) from the
corresponding &lt;a class="reference external" href="https://snapcraft.io/freeshow"&gt;snap&lt;/a&gt;. Unfortunately we can&amp;#8217;t
include that here as it&amp;#8217;s another snap that&amp;#8217;s only built for &lt;tt class="docutils literal"&gt;amd64&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s plenty in these meta-packages that won&amp;#8217;t work on a Pi even if it will
install without complaint. For instance, &lt;a class="reference external" href="https://obsproject.com/"&gt;&lt;span class="caps"&gt;OBS&lt;/span&gt; Studio&lt;/a&gt; and &lt;a class="reference external" href="https://www.blender.org/"&gt;Blender&lt;/a&gt; both
require a &lt;span class="caps"&gt;GPU&lt;/span&gt; with OpenGL 3.3 support (the Pi&amp;#8217;s only supports up to 2.1). You
can attempt to override this with &lt;tt class="docutils literal"&gt;MESA_GL_VERSION_OVERRIDE=3.3&lt;/tt&gt; in your
environment, but don&amp;#8217;t expect much in the way of performance (and also don&amp;#8217;t be
surprised if several things, like the surfaces in the Blender preview, don&amp;#8217;t
display at all, assuming you&amp;#8217;re patient enough to wait for it to&amp;nbsp;start).&lt;/p&gt;
&lt;p&gt;That said, we should talk about the performance because, once again, we&amp;#8217;re
dealing with a Plasma desktop running under Wayland (assuming you selected the
Wayland option on the login screen, which I &lt;em&gt;highly&lt;/em&gt; recommend). And, once
again, the performance (for a Pi) is great. Despite it being an environment
replete with little &lt;span class="caps"&gt;UI&lt;/span&gt; animations, they all seem to play smoothly. The
interface never feels laggy in responding to clicks or movements. Sure, there&amp;#8217;s
some delay when launching things but that&amp;#8217;s not the desktop interface; that&amp;#8217;s
the &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card.&lt;/p&gt;
&lt;img alt="A screenshot of the Ubuntu Studio desktop. In contrast to Kubuntu, the application bar is at the top (reminiscent of Xubuntu). Next to the start menu button is a large array of quick-launch icons for myriad applications (Ardour, Krita, GIMP, kdenlive, etc.) and finally at the far right is the system tray. Below the top bar are a variety of Studio's more unique applications: MuseScore showing a few bars of the opening of Scott Joplin's The Entertainer, LMMS showing a (rather bad) recording of a few opening bars of Scott Joplin's The Entertainer, htop running in a terminal, a scribble in Krita that makes XKCD look like high art, and the KDE settings application atop everything showing a summary of the system's hardware." src="https://waldorf.waveform.org.uk/images/desktops-lunar-studio.png" /&gt;
&lt;p&gt;In use Ubuntu Studio is a bit different. For starters, it was the only flavour
here to correctly select the &lt;span class="caps"&gt;HDMI&lt;/span&gt; audio output by default. Perhaps this is
something to do with the rather more complex audio stack? This is no more than
a guess on my part,&amp;nbsp;however.&lt;/p&gt;
&lt;p&gt;Studio is &lt;em&gt;by far&lt;/em&gt; the largest of the flavours here. With all the meta-packages
included above it weighs in at a positively obese &lt;span class="caps"&gt;17GB&lt;/span&gt; on the &lt;span class="caps"&gt;SD&lt;/span&gt; card, so don&amp;#8217;t
even think about trying this on anything less than a &lt;span class="caps"&gt;32GB&lt;/span&gt; card (unless you
leave out some meta-packages). Speaking of storage, the partitions on the &lt;span class="caps"&gt;SD&lt;/span&gt;
card showed up as removable in the file browser (same as in&amp;nbsp;Kubuntu).&lt;/p&gt;
&lt;p&gt;As on Kubuntu, the terminal was the the typically excellent and feature-full
&lt;a class="reference external" href="https://konsole.kde.org/"&gt;Konsole&lt;/a&gt;, and the file-browser is again &lt;a class="reference external" href="https://apps.kde.org/en-gb/dolphin/"&gt;Dolphin&lt;/a&gt;. Still, it feels a bit
churlish to look at my usual set of applications for Ubuntu Studio. No-one&amp;#8217;s
going to be installing Studio just to &lt;em&gt;play&lt;/em&gt; music or fiddle with the terminal;
it&amp;#8217;s a flavour for &lt;em&gt;creators&lt;/em&gt;. So I scoured the house for all the interesting
gear I could find to plug in and see how things went&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;First off, given we&amp;#8217;re dealing with a novel display stack (Wayland), how about
trying to colour-calibrate the monitor? I dug out my trusty old &lt;a class="reference external" href="https://github.com/hughski/colorhug2-firmware"&gt;ColorHug2&lt;/a&gt;
and strapped it to the screen, fired up &lt;a class="reference external" href="https://displaycal.net/"&gt;DisplayCAL&lt;/a&gt; and got to work. No
issues generating the profile: hardware was recognized, the screen-saver was
suppressed for the duration of calibration, and although the calculations took
a while (it is a Pi), it was ultimately successful at generating a profile.
Unfortunately, I couldn&amp;#8217;t get the profile to install at the end of it
(complaints about a timeout, though after a bit of digging I think there&amp;#8217;s
something else going on). Nor, for that matter, did the swapped-red-and-blue
test profile appear to work, so there&amp;#8217;s something else missing here. Oh&amp;nbsp;well.&lt;/p&gt;
&lt;p&gt;What about drawing? I helped myself to the ancient Wacom Intuos 3 tablet
attached to my other half&amp;#8217;s Mac Mini, fired up &lt;a class="reference external" href="https://krita.org/en/"&gt;Krita&lt;/a&gt; and drew some bits that
6 year-old me would&amp;#8217;ve been truly proud of. Ahem. Needless to say, the tablet
Just Worked, including all the fun configuration options for optional hot-keys,
pressure response curves and tool switching depending on the pen tip. I can&amp;#8217;t
say this is terribly surprising, though, as it&amp;#8217;s always worked on every Linux
system I&amp;#8217;ve tried it on for the last 10 years or&amp;nbsp;so.&lt;/p&gt;
&lt;p&gt;How about music composition? I nicked my daughter&amp;#8217;s Yamaha keyboarad, found the
one remaining &lt;span class="caps"&gt;USB&lt;/span&gt;-B cable in the house (that wasn&amp;#8217;t busy serving a &lt;span class="caps"&gt;UPS&lt;/span&gt;), and
hooked it up as a &lt;span class="caps"&gt;MIDI&lt;/span&gt; source. After failing to figure out &lt;a class="reference external" href="https://ardour.org/"&gt;Ardour&lt;/a&gt;, I managed
to rekindle some of my &lt;a class="reference external" href="https://lmms.io/"&gt;&lt;span class="caps"&gt;LMMS&lt;/span&gt;&lt;/a&gt; skills and, with some awkward fumbling around
the sound output configuration, got it recording from (and playing back
through) the keyboard. My formerly formidable piano skills have sadly
bit-rotted over the last 30 years. But I still managed to record a passable
rendition of the opening of Scott Joplin&amp;#8217;s The Entertainer … treble only, at a
positively sedate 45bpm, then the bass line on a separate track, then sped it
all up to 70 to pretend I could still&amp;nbsp;play.&lt;/p&gt;
&lt;p&gt;On the publishing side of things I gave the excellent &lt;a class="reference external" href="https://musescore.org/en"&gt;MuseScore&lt;/a&gt; a whirl,
including the keyboard as &lt;span class="caps"&gt;MIDI&lt;/span&gt; input. It performed happily and I managed to
crank out a vaguely acceptable score for the first bit of The Entertainer again
&lt;a class="footnote-reference" href="#wysiwyg-music" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. Printing remotely worked happily, as did scanning (once I&amp;#8217;d
configured &lt;a class="reference external" href="http://www.sane-project.org/"&gt;&lt;span class="caps"&gt;SANE&lt;/span&gt;&lt;/a&gt; to look on the right host for the&amp;nbsp;scanner).&lt;/p&gt;
&lt;p&gt;I gave the video editing and rendering bits a brief try, but they were pretty
much as one would expect: basically unusable without much beefier&amp;nbsp;hardware.&lt;/p&gt;
&lt;p&gt;Finally, I tried out &lt;a class="reference external" href="https://www.scribus.net/"&gt;Scribus&lt;/a&gt; as well and was rather surprised to find it
very &amp;#8220;laggy&amp;#8221; when dragging things out on the canvas. I pondered if it&amp;#8217;s one of
the applications currently suffering from Wayland transition issues and ran it
instead with &lt;tt class="docutils literal"&gt;QT_QPA_PLATFORM=xcb&lt;/tt&gt; and that &lt;em&gt;largely&lt;/em&gt; fixed things. Still, I
didn&amp;#8217;t dig any further (as I&amp;#8217;d already burned far too much time playing with
this&amp;nbsp;flavour).&lt;/p&gt;
&lt;p&gt;But now for the &lt;em&gt;burning&lt;/em&gt; question: what about the&amp;nbsp;screen-saver?&lt;/p&gt;
&lt;p&gt;It managed to lock the screen (no suspend), and then crashed. It didn&amp;#8217;t crash
back to the desktop (good marks for security), but also didn&amp;#8217;t display a
&amp;#8220;here&amp;#8217;s how to get out of this&amp;#8221; message like Kubuntu. I managed to manually use
the &lt;tt class="docutils literal"&gt;loginctl&lt;/tt&gt; trick from Kubuntu to figure out an unlock from another
console, but I can imagine an ordinary user resorting to the three-finger
shuffle here.&amp;nbsp;Ouch.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="verdict"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Verdict&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This isn&amp;#8217;t your regular flavour, and it&amp;#8217;s certain overkill for many. But if
you&amp;#8217;re the creative type it may well be the flavour for you. Provided your
expectations of the platform are reasonable (this is not the box for
&lt;a class="reference external" href="https://www.blender.org/"&gt;Blender&lt;/a&gt;, &lt;a class="reference external" href="https://obsproject.com/"&gt;&lt;span class="caps"&gt;OBS&lt;/span&gt; Studio&lt;/a&gt;, or &lt;a class="reference external" href="https://kdenlive.org/en/"&gt;kdenlive&lt;/a&gt;) it&amp;#8217;s useable for basic music
creation, composition, writing or&amp;nbsp;art.&lt;/p&gt;
&lt;p&gt;Some things aren&amp;#8217;t responsive in all circumstances (when drawing with a tablet
you have to be a bit careful of what&amp;#8217;s visible vs the complexity of the
brushes), but with a bit of patience and effort it works! Studio also has a
really nice menu detailing where to get further help, from documentation to
community&amp;nbsp;support.&lt;/p&gt;
&lt;p&gt;It looks like there&amp;#8217;s still some kinks to work out (particularly around the
Wayland transition) but yet again I&amp;#8217;m very impressed by a &lt;span class="caps"&gt;KDE&lt;/span&gt;-based&amp;nbsp;flavour.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;span id="down-here"&gt;&lt;/span&gt;&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Configuration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For Ubuntu Studio, the configurations I used were as follows. For the boot
configuration, highlighted lines are those changed from the lunar&amp;nbsp;defaults:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="na"&gt;arm_boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c1"&gt;# Enable the audio output, I2C and SPI interfaces on the GPIO header. As these&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c1"&gt;# parameters related to the base device-tree they must appear *before* any&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;# other dtoverlay= specification&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;audio=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i2c_arm=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;spi=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c1"&gt;# Comment out the following line if the edges of the desktop appear outside&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="c1"&gt;# the edges of your display&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="na"&gt;disable_overscan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="c1"&gt;# If you have issues with audio, you may try uncommenting the following line&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="c1"&gt;# which forces the HDMI output into HDMI mode instead of DVI (which doesn&amp;#39;t&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# support audio output)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;#hdmi_drive=2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="c1"&gt;# Enable the serial pins&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="c1"&gt;#enable_uart=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="c1"&gt;# Autoload overlays for any recognized cameras or displays that are attached&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="c1"&gt;# to the CSI/DSI ports. Please note this is for libcamera support, *not* for&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="c1"&gt;# the legacy camera stack&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="na"&gt;camera_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="na"&gt;display_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="c1"&gt;# Config settings specific to arm64&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="k"&gt;[cm4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="c1"&gt;# Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="c1"&gt;# such a board)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2,dr_mode=host&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the kernel command line, the entire file must consist of a &lt;em&gt;single line of
text&lt;/em&gt; so I would suggest simply copying this&amp;nbsp;wholesale:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;cmdline.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, the highlighted lines are those that you
may wish to change after copying the content. As mentioned above, you may wish
to trim the list of meta-packages&amp;nbsp;highlighted:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;studio-pi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pc105&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;raspberry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mozillateam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ppa:mozillateam/ppa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/apt/preferences.d/firefox&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=LP-PPA-mozillateam&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: 501&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: -1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-47"&gt;&lt;span class="linenos"&gt;47&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-48"&gt;&lt;span class="linenos"&gt;48&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-49"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;49&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-audio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-50"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-graphics&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-51"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;51&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-photography&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-52"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;52&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-video&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-53"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;53&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntustudio-publishing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-54"&gt;&lt;span class="linenos"&gt;54&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-55"&gt;&lt;span class="linenos"&gt;55&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, the networking configuration. I used Ethernet for my experiments, and
given the number of packages that need installing I&amp;#8217;d generally recommend that
too. Please see the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; for important information on adjusting the
network configuration to Network Manager post&amp;nbsp;installation:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;network-config&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt;4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt;5&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt;6&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="wysiwyg-music" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Hang on, if you&amp;#8217;re so gung-ho about hating &lt;a class="reference external" href="https://en.wikipedia.org/wiki/WYSIWYG"&gt;&lt;span class="caps"&gt;WYSIWYG&lt;/span&gt;&lt;/a&gt;,
shouldn&amp;#8217;t you be using &lt;a class="reference external" href="http://lilypond.org/text-input.html"&gt;LilyPond&lt;/a&gt; here? Yes, but that&amp;#8217;s not part of the
Ubuntu Studio audio seed :)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="studio"></category><category term="lunar"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>The Kubuntu Flavour</title><link href="https://waldorf.waveform.org.uk/2023/the-kubuntu-flavour.html" rel="alternate"></link><published>2023-08-05T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-08-05:/2023/the-kubuntu-flavour.html</id><summary type="html">&lt;p class="first last"&gt;Baking a Kubuntu Lunar desktop with&amp;nbsp;cloud-init&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: The &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; which links to all the other flavour posts.
Skip &lt;a class="reference internal" href="#down-here"&gt;down here&lt;/a&gt; for the configurations&amp;nbsp;used.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#8217;ll be looking at creating a Kubuntu desktop with a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, on a Raspberry&amp;nbsp;Pi.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#kubuntu" id="toc-entry-1"&gt;Kubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#verdict" id="toc-entry-2"&gt;Verdict&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#configuration" id="toc-entry-3"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="kubuntu"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Kubuntu&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Kubuntu is the &lt;span class="caps"&gt;KDE&lt;/span&gt;-based spin of Ubuntu. For those unfamiliar, &lt;span class="caps"&gt;KDE&lt;/span&gt; is the other
&amp;#8220;big&amp;#8221; desktop player besides &lt;span class="caps"&gt;GNOME&lt;/span&gt;, and is based on the Qt toolkit. &lt;span class="caps"&gt;KDE&lt;/span&gt;&amp;#8217;s
always had a more &amp;#8220;traditional&amp;#8221; feel in its desktop (almost staid, some might
say). The start bar (with a classical categorised start menu) goes at the
bottom, followed by the application switcher, and system tray. Applications all
have a reasonably common look and feel, and all applications have every
configuration option you could ever hope for scattered throughout their
numerous pages of&amp;nbsp;preferences.&lt;/p&gt;
&lt;p&gt;This is in fairly stark contrast to &lt;span class="caps"&gt;GNOME&lt;/span&gt; which, of late, has been more
experimental and has pursued a deliberate path of &amp;#8220;making it simple&amp;#8221;. To put it
another way, if you sat your elderly mother in front of a modern &lt;span class="caps"&gt;GNOME&lt;/span&gt; system,
it might take them a while to figure out how to get going. But on &lt;span class="caps"&gt;KDE&lt;/span&gt;, they&amp;#8217;d
be up and running right out the gate &lt;a class="footnote-reference" href="#simple" id="footnote-reference-1"&gt;[1]&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="A screenshot of the Kubuntu desktop. The bar at the bottom contains the Kubuntu logo at the far left, showing the opened start menu above it (with categories on the left and applications on the right). Further along the bottom bar are the quick-launch icons, then the icons of open applications, then finally the system tray on the far right. Within the desktop can be seen (from front to back), the media player showing an episode from the BBC's Hitchhikers Guide to the Galaxy TV adaptation (detailing Ford Prefect), the Dolphin file-browser, Firefox showing a page from the OwnCloud website, and in the background a PDF of a Raspberry Pi datasheet." src="https://waldorf.waveform.org.uk/images/desktops-lunar-kubuntu.png" /&gt;
&lt;p&gt;The last time I tried Kubuntu on a Pi, it was &lt;em&gt;laughably&lt;/em&gt; slow, and I was
expecting no different this time. I could not have been more wrong&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;Kubuntu is notably the only other desktop environment here that supports
Wayland out of the box. It defaults to X11, but you can easily select &amp;#8220;Desktop
Session: Plasma (Wayland)&amp;#8221; from the bottom of the &lt;span class="caps"&gt;KDM&lt;/span&gt; login page, and this
selection will persist for future logins. Speaking of logins, there&amp;#8217;s a
bizarrely long delay before the desktop shows up after login. This appears
peculiar to &lt;span class="caps"&gt;KDE&lt;/span&gt; but if you put up with it, the wait is&amp;nbsp;worthwhile!&lt;/p&gt;
&lt;p&gt;When running under Wayland, the environment is buttery-smooth. All the little
animations from the fade-in of the desktop, to the opening slide of the start
menu proceed without jitters or jerks. Don&amp;#8217;t expect any speed up in opening
large applications. This is still a Pi, and I&amp;#8217;m running these experiments off
&lt;span class="caps"&gt;SD&lt;/span&gt; cards which top out at 20Mb/s transfer on a Pi 4. Nonetheless, I was
astonished to find that the desktop I expected to be the heavy-weight laggard
was up there with the best of them in&amp;nbsp;responsiveness!&lt;/p&gt;
&lt;p&gt;The bundled applications largely work well. The file browser is the capable
&lt;a class="reference external" href="https://apps.kde.org/en-gb/dolphin/"&gt;Dolphin&lt;/a&gt; which was happy browsing and opening things from unmounted network
shares, and has a nifty &amp;#8220;multiple-selection&amp;#8221; feature (look for the &amp;#8220;plus&amp;#8221; when
hovering over items). One oddity is that, as under Lubuntu, it considered the
partitions on the &lt;span class="caps"&gt;SD&lt;/span&gt; card &amp;#8220;removable&amp;#8221; (though it mercifully didn&amp;#8217;t offer an
eject button for the root partition). One other thing it notably managed was to
extract files from an archive with drag&amp;#8217;n&amp;#8217;drop. This has been &lt;a class="reference external" href="https://gitlab.gnome.org/GNOME/file-roller/-/issues/4"&gt;broken on
&lt;span class="caps"&gt;GNOME&lt;/span&gt;&lt;/a&gt; (and thus Ubuntu, &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/file-roller/+bug/1923033"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1923033&lt;/a&gt;) for &lt;em&gt;years&lt;/em&gt;&amp;nbsp;now.&lt;/p&gt;
&lt;p&gt;As before, the default audio output is incorrectly selected, but again that&amp;#8217;s a
trivial work-around. Video playback is handled by the &lt;a class="reference external" href="https://apps.kde.org/en-gb/haruna/"&gt;Haruna&lt;/a&gt; media player.
Unfortunately this wasn&amp;#8217;t happy playing things from unmounted network shares
(despite them being accessible from the Open File menu). Otherwise, it proved
one of the more capable bundled players, handling chapter markers and subtitles
with aplomb. The bundled music player is &lt;a class="reference external" href="https://apps.kde.org/en-gb/elisa/"&gt;Elisa&lt;/a&gt; which is a simple,
attractive, and intuitive affair, but sadly doesn&amp;#8217;t support playing (or
indexing) anything other than a mounted&amp;nbsp;file-system.&lt;/p&gt;
&lt;p&gt;Firefox worked well, but now we finally come to something I alluded to at the
start. When installed as a snap it doesn&amp;#8217;t seem able to use the &amp;#8220;full&amp;#8221; Wayland
mode (reverting to XWayland protocol instead). This is the reason for
installing the deb, and also the reason for me re-writing a whole pile of stuff
here &lt;a class="footnote-reference" href="#redo1" id="footnote-reference-2"&gt;[2]&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;The screen-saver works … then sometimes crashes. At this point I was getting
rather used to flakiness in screen-savers but what surprised me was that,
having crashed, it brought up a nice and detailed set of instructions on
securely unlocking the desktop from another terminal. This … seems like an&amp;nbsp;improvement?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="verdict"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Verdict&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What a difference from last&amp;nbsp;time!&lt;/p&gt;
&lt;p&gt;Great performance (after a slow initial login), combined with &lt;span class="caps"&gt;KDE&lt;/span&gt;&amp;#8217;s legendary
configurability. Some reliability issues still (screen-saver, not-so-removable
storage!), but this is a genuinely useful desktop on a Pi&amp;nbsp;now.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s also a ton of nice little &lt;span class="caps"&gt;UI&lt;/span&gt; tweaks that made it genuinely pleasant to
use for several days. The start menu encapsulates all styles (categorical, one
big list, and searchable), the system tray has myriad capabilities that make it
far more useful than most (e.g. selecting the audio output, or bluetooth
devices straight from the tray widgets without launching other&amp;nbsp;things).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;span id="down-here"&gt;&lt;/span&gt;&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Configuration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For Kubuntu, the configurations I used were as follows. For the boot
configuration, highlighted lines are those changed from the lunar&amp;nbsp;defaults:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="na"&gt;arm_boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c1"&gt;# Enable the audio output, I2C and SPI interfaces on the GPIO header. As these&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c1"&gt;# parameters related to the base device-tree they must appear *before* any&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;# other dtoverlay= specification&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;audio=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i2c_arm=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;spi=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c1"&gt;# Comment out the following line if the edges of the desktop appear outside&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="c1"&gt;# the edges of your display&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="na"&gt;disable_overscan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="c1"&gt;# If you have issues with audio, you may try uncommenting the following line&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="c1"&gt;# which forces the HDMI output into HDMI mode instead of DVI (which doesn&amp;#39;t&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# support audio output)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;#hdmi_drive=2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="c1"&gt;# Enable the serial pins&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="c1"&gt;#enable_uart=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="c1"&gt;# Autoload overlays for any recognized cameras or displays that are attached&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="c1"&gt;# to the CSI/DSI ports. Please note this is for libcamera support, *not* for&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="c1"&gt;# the legacy camera stack&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="na"&gt;camera_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="na"&gt;display_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="c1"&gt;# Config settings specific to arm64&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="k"&gt;[cm4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="c1"&gt;# Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="c1"&gt;# such a board)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2,dr_mode=host&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the kernel command line, the entire file must consist of a &lt;em&gt;single line of
text&lt;/em&gt; so I would suggest simply copying this&amp;nbsp;wholesale:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;cmdline.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, the highlighted lines are those that you
may wish to change after copying the&amp;nbsp;content:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;kubuntu-pi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pc105&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;raspberry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mozillateam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ppa:mozillateam/ppa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/apt/preferences.d/firefox&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=LP-PPA-mozillateam&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: 501&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: -1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-47"&gt;&lt;span class="linenos"&gt;47&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-48"&gt;&lt;span class="linenos"&gt;48&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;kubuntu-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-49"&gt;&lt;span class="linenos"&gt;49&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-50"&gt;&lt;span class="linenos"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, the networking configuration. I used Ethernet for my experiments, and
given the number of packages that need installing I&amp;#8217;d generally recommend that
too. Please see the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; for important information on adjusting the
network configuration to Network Manager post&amp;nbsp;installation:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;network-config&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt;4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt;5&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt;6&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="simple" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I leave it for the reader to judge what this implies about &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;#8217;s
drive for simplicity …&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="redo1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I&amp;#8217;d started off this series doing everything &amp;#8220;by the book&amp;#8221; with
Firefox from a snap, then after discovering that only the deb-based install
supported this, wound up re-doing everything partly to ensure all the
flavours had the best performance I could give them (full Wayland support if
available without too much hacking), and secondly that they were on a level
playing field (all snaps or all debs for Firefox).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="kubuntu"></category><category term="lunar"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>The Ubuntu Unity Flavour</title><link href="https://waldorf.waveform.org.uk/2023/the-ubuntu-unity-flavour.html" rel="alternate"></link><published>2023-08-04T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-08-04:/2023/the-ubuntu-unity-flavour.html</id><summary type="html">&lt;p class="first last"&gt;Baking an Ubuntu Unity Lunar desktop with&amp;nbsp;cloud-init&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: The &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; which links to all the other flavour posts.
Skip &lt;a class="reference internal" href="#down-here"&gt;down here&lt;/a&gt; for the configurations&amp;nbsp;used.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#8217;ll be looking at creating an Ubuntu Unity desktop with a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, on a Raspberry&amp;nbsp;Pi.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#ubuntu-unity" id="toc-entry-1"&gt;Ubuntu&amp;nbsp;Unity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#verdict" id="toc-entry-2"&gt;Verdict&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#configuration" id="toc-entry-3"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="ubuntu-unity"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Ubuntu&amp;nbsp;Unity&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A blast from the past! This was one I didn&amp;#8217;t manage to test last time, but I&amp;#8217;ve
a major soft spot for good ol&amp;#8217; &lt;a class="reference external" href="https://ubuntuunity.org/"&gt;Unity&lt;/a&gt;, having spent many years with it as my
desktop on the &lt;span class="caps"&gt;PC&lt;/span&gt; back in the pre-Bionic days. So let&amp;#8217;s give it a spin on the&amp;nbsp;Pi!&lt;/p&gt;
&lt;p&gt;For those unfamiliar with Unity, it has some quite different ideas of its own:
the start menu and quick launcher are on the left of the screen (rather than
the traditional top or bottom). If I recall correctly (and I&amp;#8217;m sure someone
will correct me in the comments if I&amp;#8217;m not) this was because most screens have
a wide-screen aspect ratio, so if you&amp;#8217;re going to &amp;#8220;waste&amp;#8221; space with
non-application stuff, better to do it horizontally than vertically (which
sounds pretty reasonable to&amp;nbsp;me).&lt;/p&gt;
&lt;p&gt;In later iterations, and indeed in Unity 7 that we&amp;#8217;re using here, it also
borrows a few tid-bits from MacOS X: the window icons (minimize, maximize,
close) are on the top left, and application menus appear in the bar at the top
of the window rather than within the application window. Again, I &lt;em&gt;think&lt;/em&gt; I&amp;#8217;m
right in stating this was (at least partially) in aid of not wasting vertical&amp;nbsp;real-estate.&lt;/p&gt;
&lt;img alt="A screenshot of the Ubuntu Unity desktop. The quick-launch icons are on the left of the screen below the Ubuntu icon at the top left. At the very top is the grey bar which switches to the application menu when hovered. It also contains the system tray icons at the top right. Within the desktop can be seen Firefox, behind a file-browser, Rhythmbox, VLC playing an old episode of &amp;quot;Spaced&amp;quot;, and htop running in a terminal window." src="https://waldorf.waveform.org.uk/images/desktops-lunar-unity.png" /&gt;
&lt;p&gt;The selection of bundled applications was excellent. &lt;a class="reference external" href="https://www.videolan.org/"&gt;&lt;span class="caps"&gt;VLC&lt;/span&gt;&lt;/a&gt; for video playing,
and &lt;a class="reference external" href="https://wiki.gnome.org/Apps/Rhythmbox"&gt;Rhythmbox&lt;/a&gt; for music. Couldn&amp;#8217;t ask for better. The file browser is
the excellent &lt;a class="reference external" href="https://github.com/linuxmint/nemo"&gt;Nemo&lt;/a&gt; from the Cinnamon desktop. Everything was generally
reliable and worked …&amp;nbsp;eventually.&lt;/p&gt;
&lt;p&gt;Unfortunately, this brings us to the first major issue: performance. As with
all the alternative desktops so far there&amp;#8217;s no Wayland support yet, but even
discounting that performance was &lt;em&gt;painfully&lt;/em&gt; slow. I did note that, as under
Budgie, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cups-browsed&lt;/span&gt;&lt;/tt&gt; was eating 100% of a &lt;span class="caps"&gt;CPU&lt;/span&gt; core again. But even after
killing that, things still felt very sluggish, especially after the snappy
responses of Lubuntu and&amp;nbsp;Xubuntu.&lt;/p&gt;
&lt;p&gt;Integration was also spotty. The menus for &lt;em&gt;most&lt;/em&gt; things appeared in the top
bar. But not Firefox (arguably the most important application on the desktop).
One might argue that Firefox doesn&amp;#8217;t have a menu bar any-more, that it&amp;#8217;s
succumbed to the modern obsession with hiding everything behind a &amp;#8220;burger&amp;#8221;
button but the menus are still there: hit Alt+F and you&amp;#8217;ll see (in fact several
of the menus aren&amp;#8217;t available from the &amp;#8220;burger&amp;#8221; button, which is probably&amp;nbsp;why).&lt;/p&gt;
&lt;p&gt;However, the screen-saver? Works! This is the only other flavour to, out of the
box, reliably suspend my monitor and lock the desktop with no crashes (Lubuntu
being the&amp;nbsp;other).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="verdict"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Verdict&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;d &lt;em&gt;really&lt;/em&gt; love to recommend Unity. Its interface ideas may be novel and
quite different to most other flavours, but they make a good deal of sense (far
more than &lt;span class="caps"&gt;GNOME&lt;/span&gt;&amp;#8217;s do, to me anyway). However, while the reliability is great,
the performance just isn&amp;#8217;t good enough for use on a small&amp;nbsp;platform.&lt;/p&gt;
&lt;p&gt;The theme (whether light or dark) made it rather hard to distinguish window
edges of inactive applications. Looking at the screenshot above, can you see
where the file-browser ends, and the music player or the browser begins? There
are also some strange edges to menu corners as if the environment wanted to
draw shadows there, but&amp;nbsp;forgot:&lt;/p&gt;
&lt;img alt="A close up screen-grab of an open menu under Ubuntu Unity. The menu itself appears in a shade of grey almost indistinguishable from the white of the web-page beneath it. However, the bottom left and right corners of the menu are shaded distinctly darker, as if intended to blend with some shadow rendered around the whole menu, but which is not present." src="https://waldorf.waveform.org.uk/images/desktops-lunar-unity-menu.png" /&gt;
&lt;p&gt;This all gave me an impression that things were not rendering &amp;#8220;as intended&amp;#8221;. Is
there meant to be a shadow border around&amp;nbsp;windows?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;span id="down-here"&gt;&lt;/span&gt;&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Configuration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For Ubuntu Unity, the configurations I used were as follows. For the boot
configuration, highlighted lines are those changed from the lunar&amp;nbsp;defaults:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="na"&gt;arm_boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c1"&gt;# Enable the audio output, I2C and SPI interfaces on the GPIO header. As these&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c1"&gt;# parameters related to the base device-tree they must appear *before* any&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;# other dtoverlay= specification&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;audio=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i2c_arm=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;spi=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c1"&gt;# Comment out the following line if the edges of the desktop appear outside&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="c1"&gt;# the edges of your display&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="na"&gt;disable_overscan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="c1"&gt;# If you have issues with audio, you may try uncommenting the following line&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="c1"&gt;# which forces the HDMI output into HDMI mode instead of DVI (which doesn&amp;#39;t&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# support audio output)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;#hdmi_drive=2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="c1"&gt;# Enable the serial pins&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="c1"&gt;#enable_uart=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="c1"&gt;# Autoload overlays for any recognized cameras or displays that are attached&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="c1"&gt;# to the CSI/DSI ports. Please note this is for libcamera support, *not* for&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="c1"&gt;# the legacy camera stack&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="na"&gt;camera_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="na"&gt;display_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="c1"&gt;# Config settings specific to arm64&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="k"&gt;[cm4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="c1"&gt;# Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="c1"&gt;# such a board)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2,dr_mode=host&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the kernel command line, the entire file must consist of a &lt;em&gt;single line of
text&lt;/em&gt; so I would suggest simply copying this&amp;nbsp;wholesale:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;cmdline.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, the highlighted lines are those that you
may wish to change after copying the&amp;nbsp;content:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;unity-pi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pc105&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;raspberry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mozillateam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ppa:mozillateam/ppa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/apt/preferences.d/firefox&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=LP-PPA-mozillateam&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: 501&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: -1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-47"&gt;&lt;span class="linenos"&gt;47&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-48"&gt;&lt;span class="linenos"&gt;48&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-unity-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-49"&gt;&lt;span class="linenos"&gt;49&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-50"&gt;&lt;span class="linenos"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, the networking configuration. I used Ethernet for my experiments, and
given the number of packages that need installing I&amp;#8217;d generally recommend that
too. Please see the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; for important information on adjusting the
network configuration to Network Manager post&amp;nbsp;installation:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;network-config&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt;4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt;5&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt;6&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="unity"></category><category term="lunar"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>The Ubuntu Budgie Flavour</title><link href="https://waldorf.waveform.org.uk/2023/the-ubuntu-budgie-flavour.html" rel="alternate"></link><published>2023-08-03T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-08-03:/2023/the-ubuntu-budgie-flavour.html</id><summary type="html">&lt;p class="first last"&gt;Baking an Ubuntu Budgie for Pi desktop with&amp;nbsp;cloud-init&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: The &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; which links to all the other flavour posts.
Skip &lt;a class="reference internal" href="#down-here"&gt;down here&lt;/a&gt; for the configurations&amp;nbsp;used.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#8217;ll be looking at creating an Ubuntu Budgie desktop (with
Pi-specific customizations!) with a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#ubuntu-budgie" id="toc-entry-1"&gt;Ubuntu&amp;nbsp;Budgie&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#verdict" id="toc-entry-2"&gt;Verdict&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#configuration" id="toc-entry-3"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="ubuntu-budgie"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Ubuntu&amp;nbsp;Budgie&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Budgie desktop is (was?) based on &lt;span class="caps"&gt;GNOME&lt;/span&gt; but with a more &amp;#8220;traditional&amp;#8221;
desktop feel. The Ubuntu Budgie remix is styled with a brief nod towards MacOS
X with a Dock-like launcher (&lt;a class="reference external" href="https://launchpad.net/plank"&gt;Plank&lt;/a&gt;) centred at the bottom of the screen.
However, that&amp;#8217;s as far as the Mac-allusions go. There&amp;#8217;s no application menu in
the bar at the top, and the window icons are at the top-right instead of&amp;nbsp;top-left.&lt;/p&gt;
&lt;img alt="A screenshot of the Ubuntu Budgie desktop. Under the dark top bar containing the start-menu button, clock, and system tray, a variety of applications are shown including &amp;quot;top&amp;quot; running under the Tilix terminal, The Cult's &amp;quot;Sonic Template&amp;quot; album in the Lollypop music player, an episode of &amp;quot;Hey Duggee&amp;quot; playing in the Parole video player, and Firefox in the background showing a news article. At the bottom center of the screen is the MacOS X Dock-like application showing the icons of all the running applications, and a few others for quick launch." src="https://waldorf.waveform.org.uk/images/desktops-lunar-budgie.png" /&gt;
&lt;p&gt;Despite the &lt;span class="caps"&gt;GNOME&lt;/span&gt; heritage, there&amp;#8217;s no Wayland support yet (although a bit of
googling does suggest that it&amp;#8217;s a high priority for the next release). On first
login, I did need to select &amp;#8220;Budgie Desktop&amp;#8221; in the login manager (&lt;span class="caps"&gt;GDM&lt;/span&gt;) instead
of the &amp;#8220;Ubuntu Desktop&amp;#8221; default, which was a little odd, but it turned out
there was a reason for that which we&amp;#8217;ll come back to later&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;The specific package used to install the Ubuntu Budgie remix
(&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-budgie-desktop-raspi&lt;/span&gt;&lt;/tt&gt;) also includes a nice little Pi-specific
customization tool that launches on first login. However, I think some of its
options may be redundant at this point (for instance, when using the &amp;#8220;full&amp;#8221; &lt;span class="caps"&gt;KMS&lt;/span&gt;
overlay there&amp;#8217;s no need to specify a &lt;span class="caps"&gt;GPU&lt;/span&gt; memory size, even if you are using a
camera via the newer libcamera&amp;nbsp;stack).&lt;/p&gt;
&lt;p&gt;Most of the terminals in the various flavours are much of a muchness; all
support multiple tabs, all have scroll-back, and all are essentially decent at
their job. However, Budgie&amp;#8217;s pick for a terminal (&lt;a class="reference external" href="https://gnunn1.github.io/tilix-web/"&gt;Tilix&lt;/a&gt;) stands out as being
the only &lt;span class="caps"&gt;GTK&lt;/span&gt;-based terminal to support tiled panes, that I&amp;#8217;m aware of
&lt;a class="footnote-reference" href="#tiling" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. Last time I tried it (in 2020) the terminal (which may or may not
have been Tilix, I can&amp;#8217;t recall) crashed pretty reliably, but this time around
it was solid as any of the rest, and a stand-out feature of the&amp;nbsp;environment.&lt;/p&gt;
&lt;p&gt;Budgie seems to have a bit of a thing about tiling generally. The desktop&amp;#8217;s got
corner tiling by default (useful on a big monitor), but also has some
fascinating shortcuts for ⅖-⅗ tiling, custom (shortcut activated) layouts,
and arrangement of windows in arbitrarily sized grids. On my little monitor, I
don&amp;#8217;t really need anything more than side-by-side tiling but I can imagine this
being &lt;em&gt;very&lt;/em&gt; handy on &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/4k-60hz-on-a-pi.html"&gt;large-scale high-&lt;span class="caps"&gt;DPI&lt;/span&gt; monitors&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sadly, the reliability of the console wasn&amp;#8217;t reflected elsewhere. Things felt a
bit sluggish at times and eventually I figured out &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cups-browsed&lt;/span&gt;&lt;/tt&gt; was sitting
around eating 100% of one of the &lt;span class="caps"&gt;CPU&lt;/span&gt; cores. This &lt;em&gt;might&lt;/em&gt; be a known issue (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/cups-browsed/+bug/2018504"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;:
#2018504&lt;/a&gt;), but that&amp;#8217;s marked &amp;#8220;fix released&amp;#8221; on Lunar and this was running on
a fully up-to-date system. I&amp;#8217;ll need to try and reproduce it to see if that bug
needs more&amp;nbsp;attention.&lt;/p&gt;
&lt;p&gt;Video playback also proved tricky to test; Budgie bundles the same default
video player as Xubuntu, &lt;a class="reference external" href="https://docs.xfce.org/apps/parole/start"&gt;Parole&lt;/a&gt;, but this crashed the Budgie window manager
process on the couple of occasions I tried it (it happily kept on playing the
video, but the desktop behind flickered out of existence and then restarted!).
The bundled Music player is &lt;a class="reference external" href="https://wiki.gnome.org/Apps/Lollypop"&gt;Lollypop&lt;/a&gt; and, while it seems a capable player,
it didn&amp;#8217;t seem to have any support for any of the music sharing protocols
supported on the home server, so I wound up mounting an &lt;span class="caps"&gt;NFS&lt;/span&gt; share for the
purposes of testing. This worked, but it struggled a bit with the sheer
quantity of albums, eating a good half a gig of &lt;span class="caps"&gt;RAM&lt;/span&gt; by the time it was done
indexing. The audio output default also needed adjusting (as on the other&amp;nbsp;desktops).&lt;/p&gt;
&lt;p&gt;And the screen-saver? Didn&amp;#8217;t appear to do anything at all at first (screen
never blanked, no suspend, no lock). This turned out to be &lt;a class="reference external" href="https://discourse.ubuntubudgie.org/t/lock-screen-no-longer-works-after-22-04-upgrade/6194/5"&gt;an issue&lt;/a&gt;
with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gnome-shell-common&lt;/span&gt;&lt;/tt&gt; being installed. With that package purged (and …
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-budgie-desktop&lt;/span&gt;&lt;/tt&gt; along with it!), the screen-saver managed to lock the
desktop correctly, but still failed to suspend the monitor (same as the
official &lt;span class="caps"&gt;GNOME&lt;/span&gt; desktop). Interestingly, this also uninstalled the &lt;span class="caps"&gt;GDM3&lt;/span&gt; login
greeter, and replaced it with the &lt;a class="reference external" href="https://github.com/linuxmint/slick-greeter"&gt;slick greeter&lt;/a&gt; (familiar to users of
Unity), which resolved having to select &amp;#8220;Budgie Desktop&amp;#8221; at&amp;nbsp;login.&lt;/p&gt;
&lt;p&gt;Still, this begs the question why &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gnome-shell-common&lt;/span&gt;&lt;/tt&gt; is a transitive
dependency of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-budgie-desktop&lt;/span&gt;&lt;/tt&gt; (purging it does remove the
meta-package)? Is there a reason for this, or is it a&amp;nbsp;bug?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="verdict"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Verdict&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s attractive (nice start menu!), reasonably responsive for a modern desktop,
and has some novel applications (nice to see some Pi specific customizations).
However, it needs a bit of tweaking out of the box for better reliability. I
don&amp;#8217;t like removing seeds that supposedly define the&amp;nbsp;system!&lt;/p&gt;
&lt;p&gt;The tiling capabilities look very interesting for those on large monitors,&amp;nbsp;though.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;span id="down-here"&gt;&lt;/span&gt;&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Configuration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For Ubuntu Budgie, the configurations I used were as follows. For the boot
configuration, highlighted lines are those changed from the lunar&amp;nbsp;defaults:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="na"&gt;arm_boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c1"&gt;# Enable the audio output, I2C and SPI interfaces on the GPIO header. As these&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c1"&gt;# parameters related to the base device-tree they must appear *before* any&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;# other dtoverlay= specification&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;audio=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i2c_arm=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;spi=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c1"&gt;# Comment out the following line if the edges of the desktop appear outside&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="c1"&gt;# the edges of your display&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="na"&gt;disable_overscan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="c1"&gt;# If you have issues with audio, you may try uncommenting the following line&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="c1"&gt;# which forces the HDMI output into HDMI mode instead of DVI (which doesn&amp;#39;t&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# support audio output)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;#hdmi_drive=2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="c1"&gt;# Enable the serial pins&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="c1"&gt;#enable_uart=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="c1"&gt;# Autoload overlays for any recognized cameras or displays that are attached&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="c1"&gt;# to the CSI/DSI ports. Please note this is for libcamera support, *not* for&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="c1"&gt;# the legacy camera stack&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="na"&gt;camera_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="na"&gt;display_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="c1"&gt;# Config settings specific to arm64&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="k"&gt;[cm4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="c1"&gt;# Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="c1"&gt;# such a board)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2,dr_mode=host&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the kernel command line, the entire file must consist of a &lt;em&gt;single line of
text&lt;/em&gt; so I would suggest simply copying this&amp;nbsp;wholesale:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;cmdline.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, the highlighted lines are those that you
may wish to change after copying the&amp;nbsp;content:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;budgie-pi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pc105&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;raspberry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mozillateam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ppa:mozillateam/ppa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/apt/preferences.d/firefox&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=LP-PPA-mozillateam&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: 501&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: -1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-47"&gt;&lt;span class="linenos"&gt;47&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-48"&gt;&lt;span class="linenos"&gt;48&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-budgie-desktop-raspi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-49"&gt;&lt;span class="linenos"&gt;49&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-50"&gt;&lt;span class="linenos"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, the networking configuration. I used Ethernet for my experiments, and
given the number of packages that need installing I&amp;#8217;d generally recommend that
too. Please see the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; for important information on adjusting the
network configuration to Network Manager post&amp;nbsp;installation:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;network-config&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt;4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt;5&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt;6&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="tiling" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I specify &lt;span class="caps"&gt;GTK&lt;/span&gt;-based because that tittering you can hear is from
&lt;span class="caps"&gt;KDE&lt;/span&gt;&amp;#8217;s &lt;a class="reference external" href="https://konsole.kde.org/"&gt;Konsole&lt;/a&gt; users, who&amp;#8217;ve had this feature for yonks.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="budgie"></category><category term="lunar"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>The Xubuntu Flavour</title><link href="https://waldorf.waveform.org.uk/2023/the-xubuntu-flavour.html" rel="alternate"></link><published>2023-08-02T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-08-02:/2023/the-xubuntu-flavour.html</id><summary type="html">&lt;p class="first last"&gt;Baking a Lubuntu Lunar desktop with&amp;nbsp;cloud-init&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: The &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; which links to all the other flavour posts.
Skip &lt;a class="reference internal" href="#down-here"&gt;down here&lt;/a&gt; for the configurations&amp;nbsp;used.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#8217;ll be looking at creating a Xubuntu desktop with a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, on a Raspberry&amp;nbsp;Pi.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#xubuntu" id="toc-entry-1"&gt;Xubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#verdict" id="toc-entry-2"&gt;Verdict&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#configuration" id="toc-entry-3"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="xubuntu"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Xubuntu&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Xubuntu is another lightweight flavour of Ubuntu (similar to Lubuntu in this
respect), but based on &lt;span class="caps"&gt;XFCE&lt;/span&gt; (a &lt;span class="caps"&gt;GTK&lt;/span&gt; based environment). Subjectively, I couldn&amp;#8217;t
discern much difference in performance between Lubuntu and Xubuntu. Other than
the positioning of the application bar at the top of the screen (compared to
Lubuntu&amp;#8217;s traditional bottom placement), things felt pretty&amp;nbsp;similar.&lt;/p&gt;
&lt;img alt="A screenshot of the Xubuntu desktop. A small panel sits at the top of the screen showing (from left to right) the small applications menu button, the window switcher, and the small icons in the system tray, followed by the clock. Below are numerous overlapping windows including a PDF showing a Raspberry Pi datasheet, &amp;quot;htop&amp;quot; running in a terminal, &amp;quot;Hey Arnold!&amp;quot; playing in a video player, a file browser, one of Simon Tatham's logic puzzles, and Firefox showing the prior blog post." src="https://waldorf.waveform.org.uk/images/desktops-lunar-xubuntu.png" /&gt;
&lt;p&gt;On the functionality side of things, the incorrect audio output appeared as in
Lubuntu, but was trivial to work around. Out of the box, music playback from
the network worked thanks to the inclusion of the excellent &lt;a class="reference external" href="https://wiki.gnome.org/Apps/Rhythmbox"&gt;Rhythmbox&lt;/a&gt;
&lt;a class="footnote-reference" href="#rhythm" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. Video playback worked well with the &lt;a class="reference external" href="https://docs.xfce.org/apps/parole/start"&gt;Parole&lt;/a&gt; player, but
unfortunately it didn&amp;#8217;t seem able to playback from an &lt;span class="caps"&gt;SMB&lt;/span&gt; network share unless
it was&amp;nbsp;mounted.&lt;/p&gt;
&lt;p&gt;This is one of those &amp;#8220;integration&amp;#8221; things; the file browser (&lt;a class="reference external" href="https://docs.xfce.org/xfce/thunar/start"&gt;Thunar&lt;/a&gt;), and
most file-open dialogs, will happily browse to an &lt;span class="caps"&gt;SMB&lt;/span&gt; share and will &lt;em&gt;appear&lt;/em&gt;
to open the file. But then an error appears or, worse, nothing happens. Of
course, the solution in this case is simple: &lt;tt class="docutils literal"&gt;sudo apt install vlc&lt;/tt&gt; and it
Just&amp;nbsp;Works.&lt;/p&gt;
&lt;p&gt;On the subject of bugs, remember I mentioned screen-savers? The lock-screen
would happily kick in and blank the monitor, but wouldn&amp;#8217;t suspend it
(presumably &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/mutter/+bug/1998716"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1998716&lt;/a&gt; again?). However, attempting to unlock (wiggling
the mouse or hitting a key to bring up the login screen) gave the poor thing
conniptions. The password prompt would briefly flicker into life, then the
screen would blank again. Then flicker into life, then blank. Password entry
wouldn&amp;#8217;t work while all this was going on (something crashing?). After this
happened several (4 or 5?) times, it seemed to get a grip on itself and allow
login once more, but it&amp;#8217;s rather broken! I&amp;#8217;ll try and find some time to dig out
some more useful info for a bug report (like whether this is specific to my&amp;nbsp;monitor).&lt;/p&gt;
&lt;p&gt;Everything else was pretty much as expected: I could read PDFs, print things,
open links, browse the web, manipulate the file-system, use the terminal, all
without issue, and all reasonably&amp;nbsp;smoothly.&lt;/p&gt;
&lt;p&gt;One amusing stand-out I did notice: the choice of bundled desktop games! On
most desktops, this is pretty bland fare. Some variation of Klondike solitaire,
maybe a Minesweeper clone, perhaps a Sudoku implementation. But not here! No,
Xubuntu bundles one single game package &lt;a class="footnote-reference" href="#other-games" id="footnote-reference-2"&gt;[2]&lt;/a&gt;, but it&amp;#8217;s packed with
more fiendishly fun entertainment than all the other default desktop gaming
packages put together: &lt;a class="reference external" href="https://www.chiark.greenend.org.uk/~sgtatham/puzzles/"&gt;Simon Tatham&amp;#8217;s Puzzles&lt;/a&gt; (if you haven&amp;#8217;t played these
before, you owe it to yourself to &lt;tt class="docutils literal"&gt;sudo apt install &lt;span class="pre"&gt;sgt-launcher&lt;/span&gt;&lt;/tt&gt; and give
them a&amp;nbsp;whirl).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="verdict"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Verdict&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s pretty (rather more so that Lubuntu in my opinion), lightweight,
responsive and reasonably functional. However, it&amp;#8217;s got a few more bugs than
Lubuntu (the screen-saver particularly) so given the choice I&amp;#8217;d probably go
with Lubuntu for a light-weight choice. Still … a &lt;em&gt;brilliant&lt;/em&gt; selection of&amp;nbsp;games!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;span id="down-here"&gt;&lt;/span&gt;&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Configuration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For Xubuntu, the configurations I used were as follows. For the boot
configuration, highlighted lines are those changed from the lunar&amp;nbsp;defaults:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="na"&gt;arm_boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c1"&gt;# Enable the audio output, I2C and SPI interfaces on the GPIO header. As these&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c1"&gt;# parameters related to the base device-tree they must appear *before* any&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;# other dtoverlay= specification&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;audio=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i2c_arm=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;spi=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c1"&gt;# Comment out the following line if the edges of the desktop appear outside&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="c1"&gt;# the edges of your display&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="na"&gt;disable_overscan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="c1"&gt;# If you have issues with audio, you may try uncommenting the following line&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="c1"&gt;# which forces the HDMI output into HDMI mode instead of DVI (which doesn&amp;#39;t&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# support audio output)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;#hdmi_drive=2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="c1"&gt;# Enable the serial pins&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="c1"&gt;#enable_uart=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="c1"&gt;# Autoload overlays for any recognized cameras or displays that are attached&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="c1"&gt;# to the CSI/DSI ports. Please note this is for libcamera support, *not* for&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="c1"&gt;# the legacy camera stack&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="na"&gt;camera_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="na"&gt;display_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="c1"&gt;# Config settings specific to arm64&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="k"&gt;[cm4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="c1"&gt;# Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="c1"&gt;# such a board)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2,dr_mode=host&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the kernel command line, the entire file must consist of a &lt;em&gt;single line of
text&lt;/em&gt; so I would suggest simply copying this&amp;nbsp;wholesale:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;cmdline.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, the highlighted lines are those that you
may wish to change after copying the&amp;nbsp;content:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;xubuntu-pi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pc105&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;raspberry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mozillateam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ppa:mozillateam/ppa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/apt/preferences.d/firefox&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=LP-PPA-mozillateam&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: 501&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: -1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-47"&gt;&lt;span class="linenos"&gt;47&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-48"&gt;&lt;span class="linenos"&gt;48&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;xubuntu-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-49"&gt;&lt;span class="linenos"&gt;49&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-50"&gt;&lt;span class="linenos"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, the networking configuration. I used Ethernet for my experiments, and
given the number of packages that need installing I&amp;#8217;d generally recommend that
too. Please see the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; for important information on adjusting the
network configuration to Network Manager post&amp;nbsp;installation:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;network-config&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt;4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt;5&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt;6&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="rhythm" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Rhythmbox is probably my favourite part of the entire &lt;span class="caps"&gt;GNOME&lt;/span&gt;
project. It&amp;#8217;s genuinely superb: supports every protocol and format I&amp;#8217;ve ever
needed (and several I haven&amp;#8217;t), has a &lt;span class="caps"&gt;UI&lt;/span&gt; that&amp;#8217;s minimal but fantastically
functional, and most of all &lt;em&gt;fast&lt;/em&gt;. Want to navigate your way through ~8000
songs spread over ~500 albums? No problem, even on a Pi! It&amp;#8217;s one of the
four applications that I reliably fire up every single day.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="other-games" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;That I could see; &lt;a class="reference external" href="https://git.launchpad.net/~xubuntu-dev/ubuntu-seeds/+git/xubuntu/tree/desktop#n101"&gt;the seed&lt;/a&gt;
seems to suggest it ships the &lt;span class="caps"&gt;GNOME&lt;/span&gt; implementations of Mines and Sudoku too
but they didn&amp;#8217;t appear in the start menu.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="lubuntu"></category><category term="lunar"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>The Lubuntu Flavour</title><link href="https://waldorf.waveform.org.uk/2023/the-lubuntu-flavour.html" rel="alternate"></link><published>2023-08-01T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-08-01:/2023/the-lubuntu-flavour.html</id><summary type="html">&lt;p class="first last"&gt;Baking a Lubuntu Lunar desktop with&amp;nbsp;cloud-init&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: The &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; which links to all the other flavour posts.
Skip &lt;a class="reference internal" href="#down-here"&gt;down here&lt;/a&gt; for the configurations&amp;nbsp;used.&lt;/p&gt;
&lt;p&gt;In this post, we&amp;#8217;ll be looking at creating a Lubuntu desktop with a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, on a Raspberry&amp;nbsp;Pi.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#lubuntu" id="toc-entry-1"&gt;Lubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#verdict" id="toc-entry-2"&gt;Verdict&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#configuration" id="toc-entry-3"&gt;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="lubuntu"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Lubuntu&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lubuntu is a lightweight flavour of Ubuntu, based on LXQt (which is the desktop
environment that RaspiOS is based upon too). It&amp;#8217;s certainly the lightest in
terms of resource usage from all the desktops I&amp;#8217;m comparing, and one of the
most responsive&amp;nbsp;too.&lt;/p&gt;
&lt;img alt="A screenshot of the Lubuntu desktop. A small panel sits at the bottom of the screen showing (from left to right) the small applications menu button (adorned with a bird), the numbers 1 through 4 for the desktop switcher, some quick-launch icons for the file-manager and firefox, the window switcher, and the small icons in the system tray, followed by the clock. Above are numerous overlapping windows including a PDF showing a section of the FreeRTOS manual datasheet, &amp;quot;htop&amp;quot; running in a terminal, &amp;quot;Sintel&amp;quot; playing in VLC, a file browser, and Firefox showing the Lubuntu homepage." src="https://waldorf.waveform.org.uk/images/desktops-lunar-lubuntu.png" /&gt;
&lt;p&gt;It&amp;#8217;s a bit prettier than when I last saw it 3 years ago, but still doesn&amp;#8217;t have
the polish of the &amp;#8220;full&amp;#8221; desktop suites like &lt;span class="caps"&gt;GNOME&lt;/span&gt; or &lt;span class="caps"&gt;KDE&lt;/span&gt;. The interface feels
very responsive but it&amp;#8217;s notable that there&amp;#8217;s no Wayland support yet so the
performance of graphically heavy things is not entirely&amp;nbsp;smooth.&lt;/p&gt;
&lt;p&gt;One issue immediately jumped out, which was the incorrect selection of the
default audio output (the 3.5mm jack output which wasn&amp;#8217;t connected, instead of
&lt;span class="caps"&gt;HDMI&lt;/span&gt;). This is a known issue (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/pipewire/+bug/1993347"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1993347&lt;/a&gt;), and one that occurs on almost
every other flavour too. Thankfully it&amp;#8217;s a trivial work-around (just pick the
&lt;span class="caps"&gt;HDMI&lt;/span&gt; output instead) and the selection persists across reboots, so it&amp;#8217;s a
one-time&amp;nbsp;fix.&lt;/p&gt;
&lt;p&gt;The applications on Lubuntu are understandably minimal, but surprisingly good!
Most desktop environments try and bundle &amp;#8220;native&amp;#8221; applications for specific
purposes, like a video player. By &amp;#8220;native&amp;#8221; I mean applications that stick
strictly within that desktop&amp;#8217;s base toolkit (whether that&amp;#8217;s &lt;span class="caps"&gt;GTK&lt;/span&gt; or Qt, and
their corresponding media bits), so that the distribution of that desktop
doesn&amp;#8217;t bloat itself out with additional libraries (or whole frameworks) for a
single application. This means you&amp;#8217;ll see things like the &lt;a class="reference external" href="https://docs.xfce.org/apps/parole/start"&gt;Parole&lt;/a&gt; or &lt;a class="reference external" href="https://gitlab.gnome.org/GNOME/totem"&gt;Totem&lt;/a&gt;
video players … right before you open a command line, &lt;tt class="docutils literal"&gt;sudo apt install vlc&lt;/tt&gt;,
and never look&amp;nbsp;back.&lt;/p&gt;
&lt;p&gt;Lubuntu, by contrast, just installs &lt;a class="reference external" href="https://www.videolan.org/"&gt;&lt;span class="caps"&gt;VLC&lt;/span&gt;&lt;/a&gt; saving you a whole step! That said,
&lt;span class="caps"&gt;VLC&lt;/span&gt; is all you get for media playback here and, capable though &lt;span class="caps"&gt;VLC&lt;/span&gt; is with
music, it&amp;#8217;s not set up as an album browser. I&amp;#8217;m too used to having something to
do just that. The distro seed giveth, and the distro seed … leaveth stuff&amp;nbsp;out.&lt;/p&gt;
&lt;p&gt;There were a couple of minor oddities. The two partitions on the &lt;span class="caps"&gt;SD&lt;/span&gt; card show
up as &amp;#8220;removable&amp;#8221; (which they fairly obviously … aren&amp;#8217;t) on the system tray
widget. After one system upgrade and a reboot, the wallpaper disappeared
inexplicably. However, the screen-saver worked perfectly. Locked the screen and
put the monitor in power-saving mode. Why do I mention this? Firstly, I&amp;#8217;ve had
a long-standing issue with the official &lt;span class="caps"&gt;GNOME&lt;/span&gt; desktop not putting my monitor
into power-saving mode (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/mutter/+bug/1998716"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1998716&lt;/a&gt;). Secondly … screen-saver breakage
became a bit of a theme the further I went on with this series&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;Otherwise, things mostly worked well. The included file browser is &lt;a class="reference external" href="https://wiki.archlinux.org/title/PCManFM"&gt;PCManFM&lt;/a&gt;,
which is capable (and fast!). It&amp;#8217;ll also be familiar to users of RaspiOS as
it&amp;#8217;s the default file browser there too. No issues opening things, including
over unmounted network shares, no problems printing or scanning over the
network, and Firefox worked as&amp;nbsp;normal.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="verdict"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Verdict&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s not the prettiest, but it is lightweight, responsive, and has a nice
choice of default apps. In practice it feels functional enough that I&amp;#8217;d be
happy to use it as my desktop, but I&amp;#8217;d definitely miss the ability to easily
&amp;#8220;tile&amp;#8221; applications (maybe there&amp;#8217;s a shortcut for this that I didn&amp;#8217;t&amp;nbsp;find!).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;span id="down-here"&gt;&lt;/span&gt;&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Configuration&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For Lubuntu, the configurations I used were as follows. For the boot
configuration, highlighted lines are those changed from the lunar&amp;nbsp;defaults:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="na"&gt;arm_boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c1"&gt;# Enable the audio output, I2C and SPI interfaces on the GPIO header. As these&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c1"&gt;# parameters related to the base device-tree they must appear *before* any&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;# other dtoverlay= specification&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;audio=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i2c_arm=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;spi=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c1"&gt;# Comment out the following line if the edges of the desktop appear outside&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="c1"&gt;# the edges of your display&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="na"&gt;disable_overscan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="c1"&gt;# If you have issues with audio, you may try uncommenting the following line&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="c1"&gt;# which forces the HDMI output into HDMI mode instead of DVI (which doesn&amp;#39;t&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# support audio output)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;#hdmi_drive=2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="c1"&gt;# Enable the serial pins&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="c1"&gt;#enable_uart=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="c1"&gt;# Autoload overlays for any recognized cameras or displays that are attached&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="c1"&gt;# to the CSI/DSI ports. Please note this is for libcamera support, *not* for&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="c1"&gt;# the legacy camera stack&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="na"&gt;camera_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="na"&gt;display_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="c1"&gt;# Config settings specific to arm64&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="k"&gt;[cm4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="c1"&gt;# Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="c1"&gt;# such a board)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2,dr_mode=host&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the kernel command line, the entire file must consist of a &lt;em&gt;single line of
text&lt;/em&gt; so I would suggest simply copying this&amp;nbsp;wholesale:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;cmdline.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration, the highlighted lines are those that you
may wish to change after copying the&amp;nbsp;content:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lubuntu-pi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pc105&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;raspberry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mozillateam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ppa:mozillateam/ppa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/apt/preferences.d/firefox&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=LP-PPA-mozillateam&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: 501&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: -1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-47"&gt;&lt;span class="linenos"&gt;47&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-48"&gt;&lt;span class="linenos"&gt;48&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lubuntu-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-49"&gt;&lt;span class="linenos"&gt;49&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-50"&gt;&lt;span class="linenos"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, the networking configuration. I used Ethernet for my experiments, and
given the number of packages that need installing I&amp;#8217;d generally recommend that
too. Please see the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html"&gt;intro post&lt;/a&gt; for important information on adjusting the
network configuration to Network Manager post&amp;nbsp;installation:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;network-config&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt;4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt;5&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt;6&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="lubuntu"></category><category term="lunar"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>We Serve All Flavours!</title><link href="https://waldorf.waveform.org.uk/2023/we-serve-all-flavours.html" rel="alternate"></link><published>2023-07-31T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-07-31:/2023/we-serve-all-flavours.html</id><summary type="html">&lt;p class="first last"&gt;More tricks with cloud-init for baking ready-made desktops, and the
index for the forthcoming look at each of the Ubuntu&amp;nbsp;flavours&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: The &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/desktop-on-a-stick.html"&gt;prior post&lt;/a&gt; in which we learned about the wonders of
automated configuration with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s that time of the decade, again: desktop round-up time! As of writing this
introductory paragraph, I&amp;#8217;ve now finished poking, prodding, and reviewing each
of the Ubuntu flavours. However, the article turned into yet another
small-novella sized piece and the only sensible way I can think to publish this
is by making this one an introductory piece, with a few final &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt;
hints, and an index to the other articles which will be published once a day
over the coming week (and a&amp;nbsp;bit).&lt;/p&gt;
&lt;p&gt;My apologies, dear reader, for keeping you on tenterhooks again&amp;nbsp;…&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#adding-sprinkles" id="toc-entry-1"&gt;Adding&amp;nbsp;Sprinkles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#flakes" id="toc-entry-2"&gt;99&amp;nbsp;Flakes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#the-taste-test" id="toc-entry-3"&gt;The Taste&amp;nbsp;Test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#the-flavours" id="toc-entry-4"&gt;The&amp;nbsp;Flavours&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="adding-sprinkles"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;Adding&amp;nbsp;Sprinkles&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the interests of &amp;#8220;integration&amp;#8221;, and because I wish to demonstrate yet more
capabilities of &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest/"&gt;cloud-init&lt;/a&gt;, I&amp;#8217;m going to pre-emptively work around a
long-standing bug (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/evince/+bug/1794064"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1974064&lt;/a&gt;) which I frequently run into, preventing
links in PDFs from opening in the browser. But only if that browser is a snap …
as Firefox is. Furthermore (without giving too much away from future posts in
this series) there still appear to be some integration issues between the
snapped Firefox and Wayland based environments in some of the desktops. So, in
the interests of a level playing field for everyone we&amp;#8217;re going to get
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; to install Firefox from a deb on first&amp;nbsp;boot.&lt;/p&gt;
&lt;p&gt;We need to add an &lt;tt class="docutils literal"&gt;apt&lt;/tt&gt; source (the Firefox stable &lt;span class="caps"&gt;PPA&lt;/span&gt;), and add some
configuration to pin its items higher than the archive&amp;#8217;s. Thankfully, this is
pretty simple to derive from all &lt;a class="reference external" href="https://askubuntu.com/a/1404401"&gt;those&lt;/a&gt;
&lt;a class="reference external" href="https://www.omgubuntu.co.uk/2022/04/how-to-install-firefox-deb-apt-ubuntu-22-04"&gt;guides&lt;/a&gt;
out there showing how to do this on an already running&amp;nbsp;system:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mozillateam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ppa:mozillateam/ppa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/apt/preferences.d/firefox&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=LP-PPA-mozillateam&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: 501&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: -1&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can ditch that weird &lt;tt class="docutils literal"&gt;snap&lt;/tt&gt; section in our configuration, and just
stick &lt;tt class="docutils literal"&gt;firefox&lt;/tt&gt; in the list of packages to&amp;nbsp;install.&lt;/p&gt;
&lt;p&gt;One other tip I&amp;#8217;m going to add here, but which I won&amp;#8217;t be adding to the
experimental configurations as published, because the &lt;em&gt;vast&lt;/em&gt; majority of people
won&amp;#8217;t want this, is configuration for an &lt;tt class="docutils literal"&gt;apt&lt;/tt&gt; proxy (specifically
&lt;a class="reference external" href="https://wiki.debian.org/AptCacherNg"&gt;apt-cacher-ng&lt;/a&gt;). To be clear, for most people I don&amp;#8217;t recommend running this
(a couple of times a month it sends my server into a bit of a tizz and needs
kicking), but if you&amp;#8217;re going to be repeatedly installing the same large
packages (as I frequently do for packaging work, or in this case when
installing several desktop packages which have similar bases) it can save a
huge amount of time and bandwidth. Anyway, this is what the relevant snippet
looks&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;Acquire::http { Proxy &amp;quot;http://myproxy.local:3142&amp;quot;; }&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Obviously, replace &lt;tt class="docutils literal"&gt;myproxy.local&lt;/tt&gt; with the address of your local&amp;nbsp;proxy.&lt;/p&gt;
&lt;p&gt;Below is the full configuration we&amp;#8217;ll be using (minus the apt-cacher
configuration), with a fairly obvious placeholder for the desktop package. I&amp;#8217;ll
publish full configurations (including the &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt;, and
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;network-config&lt;/span&gt;&lt;/tt&gt;, largely derived from the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/desktop-on-a-stick.html"&gt;prior post&lt;/a&gt;) in each section in
case you want to play along at home. Just place these on the boot partition of
an Ubuntu Server for Pi (23.04 or later) card &lt;a class="footnote-reference" href="#arch" id="footnote-reference-1"&gt;[1]&lt;/a&gt; and adjust the
highlighted lines to your&amp;nbsp;tastes:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;miss-piggy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pc105&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ctrl:nocaps&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;raspberry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mozillateam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ppa:mozillateam/ppa&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/apt/preferences.d/firefox&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=LP-PPA-mozillateam&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: 501&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Package: firefox*&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin: release o=Ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;Pin-Priority: -1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-47"&gt;&lt;span class="linenos"&gt;47&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-48"&gt;&lt;span class="linenos"&gt;48&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-49"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;49&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;YOUR-DESKTOP-PACKAGE-HERE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-50"&gt;&lt;span class="linenos"&gt;50&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-51"&gt;&lt;span class="linenos"&gt;51&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Bear in mind your first boot is going to install a &lt;em&gt;lot&lt;/em&gt; of things, and it will
take &lt;em&gt;at least&lt;/em&gt; an hour (more like an hour and a half) even on a good
A1-compliant &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="flakes"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;99&amp;nbsp;Flakes&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One wrinkle I forgot to mention in the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/desktop-on-a-stick.html"&gt;prior post&lt;/a&gt; is that of networking. The
server images we&amp;#8217;re basing this on use &lt;a class="reference external" href="https://netplan.io/"&gt;netplan&lt;/a&gt;, and start off by having it
&amp;#8220;render&amp;#8221; the network configuration via &lt;a class="reference external" href="https://www.freedesktop.org/software/systemd/man/systemd.network.html"&gt;networkd&lt;/a&gt; (part of&amp;nbsp;systemd).&lt;/p&gt;
&lt;p&gt;However, every Ubuntu desktop flavour ultimately winds up using &lt;a class="reference external" href="https://networkmanager.dev/"&gt;Network
Manager&lt;/a&gt; for its network configuration. While &lt;a class="reference external" href="https://netplan.io/"&gt;netplan&lt;/a&gt; is &lt;em&gt;capable&lt;/em&gt; of
rendering a configuration via Network Manager, that&amp;#8217;s probably &lt;em&gt;not&lt;/em&gt; what you
ultimately want as your network configuration wouldn&amp;#8217;t be editable via the
Network Manager &lt;span class="caps"&gt;GUI&lt;/span&gt; (only the netplan &lt;span class="caps"&gt;YAML&lt;/span&gt; configuration&amp;nbsp;files).&lt;/p&gt;
&lt;p&gt;Why not change the initial netplan configuration to render through Network
Manager instead? This won&amp;#8217;t work because at first boot, the card is just Ubuntu
Server for Pi and doesn&amp;#8217;t contain Network Manager. That only gets installed
along with the chosen desktop flavour, but &lt;em&gt;that&lt;/em&gt; act requires a network to
already be configured. Dependency&amp;nbsp;cycles!&lt;/p&gt;
&lt;p&gt;The fix is simple enough. Once the desktop has booted, and you&amp;#8217;re all snugly
logged in for the first time, run the following to delete the generated netplan
configuration and re-apply the (now empty)&amp;nbsp;settings:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;/etc/netplan/50-cloud-init.yaml
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;netplan&lt;span class="w"&gt; &lt;/span&gt;apply
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At this point networkd will stop &amp;#8220;managing&amp;#8221; the interfaces and Network Manager
will take them over. In the case of a simple &lt;span class="caps"&gt;DHCP&lt;/span&gt;-based Ethernet connection, it
should reconfigure automatically and you&amp;#8217;ll be back up and running in seconds.
However, if you performed your initial set up over WiFi you&amp;#8217;ll now need to go
into the &lt;span class="caps"&gt;GUI&lt;/span&gt; network settings for your flavour (wherever that is — it can
usually be found from the system-tray though) and re-enter your WiFi&amp;nbsp;credentials.&lt;/p&gt;
&lt;p&gt;This is one of the ways that this Frankenstein-like desktop setup differs from
the official desktop releases. There are a few others (dual seeds, user
creation differences, root-fs sizing, probably a few others that I&amp;#8217;ve
forgotten!), and if anyone&amp;#8217;s interested I can go through them, but this is the
only one that&amp;#8217;s affected the use of the desktop during my&amp;nbsp;explorations.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-taste-test"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;The Taste&amp;nbsp;Test&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What should I look for in a desktop? I can&amp;#8217;t define this universally, so I&amp;#8217;ll
just go with what works for me. My personal workflow is probably a strange mix
of deeply anachronistic, and semi-modern. On the one hand, I spend the &lt;em&gt;vast&lt;/em&gt;
majority of my time in a terminal window. On the other, I eschew office
applications in favour of doing office-y work in a web-browser. Google Sheets
for any spreadsheet work (though I have been known to dip into &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Sc_(spreadsheet_calculator)"&gt;sc&lt;/a&gt; for
trivial bits), and when I absolutely &lt;em&gt;have&lt;/em&gt; to touch a word-processor &lt;a class="footnote-reference" href="#word" id="footnote-reference-2"&gt;[2]&lt;/a&gt;,
Google&amp;nbsp;Docs.&lt;/p&gt;
&lt;p&gt;I could &lt;em&gt;almost&lt;/em&gt; get away with working on a Chromebook, but not &lt;em&gt;quite&lt;/em&gt;. If I
have PDFs to deal with (and between Canonical&amp;#8217;s hiring push, and various
hardware data-sheets, I get through quite a few of these), I use an
honest-to-goodness standalone &lt;span class="caps"&gt;PDF&lt;/span&gt; reader, instead of the (usually horribly
slow) built-in &lt;span class="caps"&gt;PDF&lt;/span&gt; readers in browsers. Sometimes I even … brace yourself …
&lt;em&gt;print&lt;/em&gt; them &lt;a class="footnote-reference" href="#printing" id="footnote-reference-3"&gt;[3]&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I also play most of my music, served from the home server, in a standalone
application, and the same goes for most films I watch (there are some good
web-based systems for this, but browsers are &lt;em&gt;heavy&lt;/em&gt; and I work on a Pi so I
tend to try and limit my use of them to text-based duties). Most of my file
manipulation is done from the command line, but sometimes for simpler stuff
(especially involving external storage) I&amp;#8217;ll resort to the file&amp;nbsp;browser.&lt;/p&gt;
&lt;p&gt;So, I&amp;#8217;ll be looking&amp;nbsp;for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A decent terminal — this is pretty much a given; it&amp;#8217;d hardly be a Linux
distro without a decent&amp;nbsp;terminal!&lt;/li&gt;
&lt;li&gt;A decent browser — again, pretty much a given as we&amp;#8217;ll be installing Firefox&amp;nbsp;anyway.&lt;/li&gt;
&lt;li&gt;Some decent media applications — music player, video player, a standalone &lt;span class="caps"&gt;PDF&lt;/span&gt;
viewer, preferably a good file browser for managing it&amp;nbsp;all.&lt;/li&gt;
&lt;li&gt;Integration — I want to be able to click a link from my terminal, or in a
&lt;span class="caps"&gt;PDF&lt;/span&gt;, and have it open in the web-browser. I&amp;#8217;d like to be able to open files,
archives, and videos straight from the file-browser, even from network shares
(&lt;em&gt;ideally&lt;/em&gt; without messing around with network mounts). The home server has
all sorts of services for this (&lt;span class="caps"&gt;NFS&lt;/span&gt;, &lt;span class="caps"&gt;SMB&lt;/span&gt;, &lt;span class="caps"&gt;DLNA&lt;/span&gt;, etc. all advertised over
avahi) so there should be plenty of opportunity here. I also want to be able
to print stuff without jumping through tons of hoops (again, the printers
here are network-accessible and advertised over&amp;nbsp;avahi).&lt;/li&gt;
&lt;li&gt;Performance — it&amp;#8217;s a Pi, so I can&amp;#8217;t expect the world here, but the video
player should handle 720p or 1080p sources in full-screen, smoothly. The
music player should play without stuttering, and generally things shouldn&amp;#8217;t
feel (too) sluggish. The browser is an exception here: modern browsers are
ridiculously (over-)complex beasts and I&amp;#8217;ll allow some lee-way for&amp;nbsp;this.&lt;/li&gt;
&lt;li&gt;Reliability — The more bugs I run into, the more points I&amp;#8217;ll dock. Erm … are
we doing points? Well, any bugs I run into I&amp;#8217;ll point out. And possibly
report, if I can figure out what to file &amp;#8216;em&amp;nbsp;against!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, with all that extra gubbins out of the way, on with the&amp;nbsp;tests!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-flavours"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#contents"&gt;The&amp;nbsp;Flavours&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These are the flavours I&amp;#8217;m intending to cover, which will become links to the
relevant&amp;nbsp;article:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/the-lubuntu-flavour.html"&gt;Lubuntu&lt;/a&gt; — the LXQt&amp;nbsp;flavour&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/the-xubuntu-flavour.html"&gt;Xubuntu&lt;/a&gt; — the &lt;span class="caps"&gt;XFCE&lt;/span&gt;&amp;nbsp;flavour&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/the-ubuntu-budgie-flavour.html"&gt;Ubuntu Budgie&lt;/a&gt; — the Budgie&amp;nbsp;flavour&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/the-ubuntu-unity-flavour.html"&gt;Ubuntu Unity&lt;/a&gt; — the Unity&amp;nbsp;flavour&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/the-kubuntu-flavour.html"&gt;Kubuntu&lt;/a&gt; — the &lt;span class="caps"&gt;KDE&lt;/span&gt;&amp;nbsp;flavour&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/the-ubuntu-studio.html"&gt;Ubuntu Studio&lt;/a&gt; — the creator&amp;#8217;s&amp;nbsp;flavour&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/the-ubuntu-desktop.html"&gt;Ubuntu&lt;/a&gt; — comparison to the official &lt;span class="caps"&gt;GNOME&lt;/span&gt;-based&amp;nbsp;image&lt;/li&gt;
&lt;/ul&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="arch" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Side note: theoretically this should work on either armhf or arm64,
however I&amp;#8217;m only testing arm64 here as it&amp;#8217;s all we officially support on the
desktop images anyway.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="word" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In case it&amp;#8217;s not obvious, I am firmly of the opinion that &lt;a class="reference external" href="https://en.wikipedia.org/wiki/WYSIWYG"&gt;&lt;span class="caps"&gt;WYSIWYG&lt;/span&gt;&lt;/a&gt;
is the work of the devil, and the One True Way is that of the markup
language, beautifully processed by 100% natural, hand-crafted,
hypo-allergenic, non-genetically modified, organically-certified algorithms
into whatever output format you may prefer.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="printing" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I have rarely felt as old as I did a few weeks ago when a
younger colleague casually inquired, &amp;#8220;does anyone still &lt;em&gt;have&lt;/em&gt; a printer?&amp;#8221;
Yes! In my utter extravagance I even have two. One of them does &lt;em&gt;colour&lt;/em&gt;!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="lunar"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>Desktop on a Stick</title><link href="https://waldorf.waveform.org.uk/2023/desktop-on-a-stick.html" rel="alternate"></link><published>2023-06-30T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-06-30:/2023/desktop-on-a-stick.html</id><summary type="html">&lt;p class="first last"&gt;Construct a variety of desktop flavours using cloud-init on the
Raspberry&amp;nbsp;Pi&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;Updated&lt;/strong&gt; to include the per-release quirks channel for the&amp;nbsp;snap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;&lt;/strong&gt;: Skip &lt;a class="reference internal" href="#down-here"&gt;down here&lt;/a&gt; for the final&amp;nbsp;configurations.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s that time of the decade, again: desktop round-up time! But thanks to
a genius notion from Oliver (one of our product managers), I&amp;#8217;m going to be
doing it a bit differently. Previously, my procedure for checking out the
&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/ubuntu-desktops-on-the-pi.html"&gt;various Ubuntu desktops on a Raspberry Pi&lt;/a&gt; looked something like&amp;nbsp;this:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Flash a server image (go make&amp;nbsp;coffee)&lt;/li&gt;
&lt;li&gt;Boot the server image (twiddle thumbs for a minute or two while it sorts out
the first time&amp;nbsp;setup)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;apt install &lt;span class="pre"&gt;lubuntu-desktop&lt;/span&gt;&lt;/tt&gt; (go make more&amp;nbsp;coffee)&lt;/li&gt;
&lt;li&gt;Reboot, play with it a bit, install a bunch of stuff (yet more&amp;nbsp;coffee)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To avoid the inevitable caffeine poisoning that results from repeating this
procedure for Lubuntu, Xubuntu, Kubuntu, and Budgie, this time we&amp;#8217;re going to
use … drum roll please&amp;nbsp;…&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/cloud-init.svg" style="width: 50%;" type="image/svg+xml"&gt;The (original) cloud-init logo with the words &amp;#8220;cloud-init&amp;#8221; adjacent.
The logo is the usual Ubuntu-orange circle, within which appears a
very geometrically simplified representation of the fingers of the
left hand. The index finger is extended and appears to be touching
the edge of a large white spark (which in no way suggests someone
electrocuting themselves by touching a live surface). Or it could
be a worm with a large anime hair-do. I&amp;#8217;m not sure.&lt;/object&gt;
&lt;p&gt;If that sounds like a bit of an anti-climax, that&amp;#8217;s just because you haven&amp;#8217;t
played with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; enough. Yes, its one-shot nature makes it painful to
iterate when you get things wrong. Yes, the documentation is painfully obscure
in places. However, once you get a grip on it, it&amp;#8217;s quite astonishingly
flexible. Hopefully, by reading this you can bypass some of that pain and start
from a more useful&amp;nbsp;point!&lt;/p&gt;
&lt;p&gt;But before we get to the fun bits, we need to get the basics done&amp;nbsp;…&lt;/p&gt;
&lt;div class="section" id="ingredients"&gt;
&lt;h2&gt;Ingredients&lt;/h2&gt;
&lt;p&gt;The first step is to get a Pi to test with. You&amp;#8217;ll need a &lt;a class="reference external" href="https://www.raspberrypi.com/products/raspberry-pi-4-model-b/"&gt;Pi 4&lt;/a&gt;, &lt;a class="reference external" href="https://www.raspberrypi.com/products/raspberry-pi-400/"&gt;Pi 400&lt;/a&gt;,
or &lt;a class="reference external" href="https://www.raspberrypi.com/products/compute-module-4/"&gt;&lt;span class="caps"&gt;CM4&lt;/span&gt;&lt;/a&gt; with at least &lt;span class="caps"&gt;4GB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt;. At the time of writing, that&amp;#8217;s a slightly
tricky proposition given the supply shortages. The vital &lt;a class="reference external" href="https://rpilocator.com/"&gt;rpilocator&lt;/a&gt; can help
here, but it would appear (at least &lt;a class="reference external" href="https://www.youtube.com/watch?v=-_aL9V0JsQQ"&gt;according to Eben&lt;/a&gt;, who probably knows
what he&amp;#8217;s talking about!) that the shortage should begin to clear during the
second half of&amp;nbsp;2023.&lt;/p&gt;
&lt;p&gt;The second step is to get an image we can play with. I&amp;#8217;m going to be using the
recently released &lt;a class="reference external" href="http://cdimage.ubuntu.com/releases/23.04/release/ubuntu-23.04-preinstalled-server-arm64+raspi.img.xz"&gt;Ubuntu 23.04 (Lunar Lobster) Server for Raspberry Pi
image&lt;/a&gt;, specifically the arm64 variant. You can use &lt;a class="reference external" href="https://www.raspberrypi.com/software/"&gt;rpi-imager&lt;/a&gt; to flash
this to a spare &lt;span class="caps"&gt;SD&lt;/span&gt; card, or even &lt;span class="caps"&gt;SSD&lt;/span&gt; drive attached to a &lt;span class="caps"&gt;USB&lt;/span&gt;&amp;nbsp;adapter.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p class="last"&gt;A word of caution: don&amp;#8217;t try this with Jammy (22.04). There&amp;#8217;s a reason I&amp;#8217;m
using Lunar (23.04) for this experiment. If you don&amp;#8217;t care about the
reasons, &lt;a class="reference internal" href="#common-stuff"&gt;skip to the next section&lt;/a&gt;. Otherwise, read on&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Historically, Ubuntu images (including the Ubuntu for Raspberry Pi images) have
been built with a system called &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;livecd-rootfs&lt;/span&gt;&lt;/tt&gt;. This could &lt;em&gt;charitably&lt;/em&gt; be
described as a rough collection of dozens of hooks (and hacks) in the form of
shell scripts, all of them plastered onto the already crufty base of
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;livecd-rootfs&lt;/span&gt;&lt;/tt&gt;, itself a gigantic mess of shell&amp;nbsp;script.&lt;/p&gt;
&lt;p&gt;The result is exactly the hellish nightmare of obscure regex-loaded nonsense
that you might imagine. Worse still, many of the hacks inject configuration
files into the resulting image that are &lt;em&gt;unowned&lt;/em&gt;. That is to say, no Debian
package owns these injected files, and that causes all sorts of fun when it
comes to&amp;nbsp;upgrades.&lt;/p&gt;
&lt;p&gt;My friend William, before he left, made a valiant start on sorting out this
mess by re-writing the old &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-image&lt;/span&gt;&lt;/tt&gt; tool. In concert with this effort,
my small contribution was to start migrating hacks from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;livecd-rootfs&lt;/span&gt;&lt;/tt&gt; into
the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-settings&lt;/span&gt;&lt;/tt&gt; package so that in Lunar (and beyond), as much
configuration as possible would be directly under the control of the package
management system (this contribution made a screeching U-turn when, at the last
minute, it was decided we &lt;em&gt;were&lt;/em&gt; going to use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;livecd-rootfs&lt;/span&gt;&lt;/tt&gt; for lunar after
all, but that&amp;#8217;s another&amp;nbsp;story!).&lt;/p&gt;
&lt;p&gt;This is one of the major reasons it&amp;#8217;s now much simpler to spin up a desktop
image with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt;. Instead of having a write a ton of configuration to
add all the necessary tweaks &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;livecd-rootfs&lt;/span&gt;&lt;/tt&gt; used to do, we can just include
one extra package and (mostly) be&amp;nbsp;done.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="preparing-your-kitchen"&gt;
&lt;span id="common-stuff"&gt;&lt;/span&gt;&lt;h2&gt;Preparing your&amp;nbsp;kitchen&lt;/h2&gt;
&lt;p&gt;Once your &lt;span class="caps"&gt;SD&lt;/span&gt; card (or drive) is flashed, eject it, then re-insert it.
Regardless of the &lt;span class="caps"&gt;OS&lt;/span&gt; you&amp;#8217;re using (Windows, Mac &lt;span class="caps"&gt;OS&lt;/span&gt;, Linux), you should see the
boot partition (labelled &amp;#8220;system-boot&amp;#8221;) appear &lt;a class="footnote-reference" href="#linux-mount" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. We&amp;#8217;re going to
be tweaking several configuration files,&amp;nbsp;specifically:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; &amp;#8212; this is the Pi bootloader&amp;nbsp;configuration&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; &amp;#8212; this is the Linux kernel command&amp;nbsp;line&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; &amp;#8212; this is the main &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; configuration&amp;nbsp;file&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;network-config&lt;/span&gt;&lt;/tt&gt; &amp;#8212; this is the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; network&amp;nbsp;configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;#8217;ll start with &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; because the changes here are universal: they&amp;#8217;ll
be required no matter which desktop we&amp;#8217;re intending to&amp;nbsp;use:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Find the &lt;tt class="docutils literal"&gt;enable_uart=1&lt;/tt&gt; line (it&amp;#8217;s on line 28 by default) and comment it
out by inserting &lt;tt class="docutils literal"&gt;#&lt;/tt&gt; at the start of the line. This simply disables the
serial console which isn&amp;#8217;t &lt;em&gt;strictly&lt;/em&gt; necessary, but if the serial console
is enabled, the Pi&amp;#8217;s &lt;span class="caps"&gt;GPU&lt;/span&gt; clock is locked to low speed and obviously we want
the &lt;span class="caps"&gt;GPU&lt;/span&gt; clock to float higher for our&amp;nbsp;desktop.&lt;/li&gt;
&lt;li&gt;At the end of the file add &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dtoverlay=vc4-kms-v3d&lt;/span&gt;&lt;/tt&gt; to load the &amp;#8220;full&amp;#8221;
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Direct_Rendering_Manager#Kernel_mode_setting"&gt;&lt;span class="caps"&gt;KMS&lt;/span&gt;&lt;/a&gt; device-tree overlay (it should be in the &lt;tt class="docutils literal"&gt;[all]&lt;/tt&gt; section at the end
of the&amp;nbsp;file)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next we need to tweak the kernel command line in &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt;. Again, all
the changes here are common to all desktops. This file consists of a single
line of text; don&amp;#8217;t introduce any line breaks when editing&amp;nbsp;it:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Delete &lt;tt class="docutils literal"&gt;console=serial0,115200&lt;/tt&gt; at the start. This just removes the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Tty_(Unix)"&gt;tty&lt;/a&gt;
associated with the serial console which we disabled above in
&lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Insert &lt;tt class="docutils literal"&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd&lt;/tt&gt; at the
start. This activates the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/6-months-with-the-pi-desktop.html#swap-swap-glorious-zswap"&gt;zswap&lt;/a&gt; system that we use on the&amp;nbsp;desktop.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These changes more or less boil down to the base differences between the Ubuntu
Pi server and desktop images. Most of the other differences (since Lunar, as
discussed above) come down to the selection of packages that are installed&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="picking-your-flavour"&gt;
&lt;h2&gt;Picking your&amp;nbsp;flavour&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s time to learn what we can do with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt;. The most obvious thing
we want is to have a particular desktop package installed. This is pretty
simple, and achieved by adding the following block to the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; file on
the boot&amp;nbsp;partition:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;lubuntu-desktop&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This tells &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; that, on first boot, we want it to try and install
two extra&amp;nbsp;packages:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lubuntu-desktop&lt;/span&gt;&lt;/tt&gt; is the basis of the Lubuntu desktop images; we&amp;#8217;ll
substitute other desktop packages here&amp;nbsp;later&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;&lt;/tt&gt; contains all the little tweaks for the Pi
desktop (ensuring there&amp;#8217;s a swap-file, disabling suspend in Gnome, inserting
the correct modules for zswap into the initrd, setting appropriate
permissions for &lt;span class="caps"&gt;I2C&lt;/span&gt; and &lt;span class="caps"&gt;SPI&lt;/span&gt; interfaces,&amp;nbsp;etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to these we &lt;em&gt;should&lt;/em&gt; also add the following settings (these aren&amp;#8217;t
mandatory, but highly&amp;nbsp;recommended):&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The first line tells &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; we want it to run &lt;tt class="docutils literal"&gt;apt update&lt;/tt&gt; to refresh
the set of available packages. This is the most important line as, without it,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; will likely fail at some point because the apt index shipped on
the image will be out of date. The next line demands that &lt;tt class="docutils literal"&gt;apt upgrade&lt;/tt&gt;
should be run to ensure all packages are up to date. The final line indicates
that, if any package indicates a reboot is required, we should carry one out
(this is absolutely the case when installing a desktop, as a display manager
will be installed at the very&amp;nbsp;least).&lt;/p&gt;
&lt;p&gt;Next, while some of the desktops available install a browser by default (by
depending on the Firefox apt package, which in turns installs the Firefox
snap), several don&amp;#8217;t as their official images include the Firefox (or Chromium)
snap directly without the apt &amp;#8220;wrapper&amp;#8221;. To avoid winding up with a desktop
without a browser, we&amp;#8217;ll add another section to install the Firefox snap&amp;nbsp;explicitly:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;snap install --channel=latest/stable/ubuntu-23.04 firefox&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I&amp;#8217;m not entirely sure why the &lt;tt class="docutils literal"&gt;snap&lt;/tt&gt; section is so different from
&lt;tt class="docutils literal"&gt;packages&lt;/tt&gt;, or why the full &lt;tt class="docutils literal"&gt;snap install &amp;lt;package&amp;gt;&lt;/tt&gt; form is required.
It does appear from the documentation that the &lt;tt class="docutils literal"&gt;snap.commands&lt;/tt&gt; section simply
allows arbitrary commands to be specified (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;canonical-livepatch&lt;/span&gt;&lt;/tt&gt; is included
in the examples), but in that case why call it a &lt;tt class="docutils literal"&gt;snap&lt;/tt&gt; section?&lt;/p&gt;
&lt;p&gt;The exceedingly long channel name causes the snap to follow a &lt;a class="reference external" href="https://snapcraft.io/docs/publish-to-a-branch"&gt;branch of a
channel&lt;/a&gt; that may include per-release quirks. Or … something like that. I&amp;#8217;m
still not clear on the whole &amp;#8220;channel&amp;#8221; concept in&amp;nbsp;snaps.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="locale-ly-sourced"&gt;
&lt;h2&gt;Locale-ly&amp;nbsp;sourced&lt;/h2&gt;
&lt;p&gt;On the official Ubuntu Desktop for Raspberry Pi images, we have a first time
setup process &lt;a class="footnote-reference" href="#oem-config" id="footnote-reference-2"&gt;[2]&lt;/a&gt; which guides the user through locale selection and
initial user creation. We don&amp;#8217;t have that here, so we&amp;#8217;d better get
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; to handle that instead. Firstly the locale, timezone, and
keyboard&amp;nbsp;settings:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;pc105&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;gb&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ctrl:nocaps&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;You may want to leave off that &amp;#8220;options:&amp;#8221; line. I include it to put Ctrl in
the right place (i.e. where Caps Lock is) and because I have no need of a
Caps Lock&amp;nbsp;key.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To find out the valid values for these settings, you can use the following
commands on an existing Ubuntu desktop (I say desktop specifically because the
available locales on server installations tend to be extremely minimal)
&lt;a class="footnote-reference" href="#no-systemd" id="footnote-reference-3"&gt;[3]&lt;/a&gt;:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;locale:&lt;/dt&gt;
&lt;dd&gt;&lt;tt class="docutils literal"&gt;localectl &lt;span class="pre"&gt;list-locales&lt;/span&gt;&lt;/tt&gt;&lt;/dd&gt;
&lt;dt&gt;timezone:&lt;/dt&gt;
&lt;dd&gt;&lt;tt class="docutils literal"&gt;timedatectl &lt;span class="pre"&gt;list-timezones&lt;/span&gt;&lt;/tt&gt;&lt;/dd&gt;
&lt;dt&gt;keyboard:&lt;/dt&gt;
&lt;dd&gt;&lt;dl class="first last docutils"&gt;
&lt;dt&gt;model:&lt;/dt&gt;
&lt;dd&gt;&lt;tt class="docutils literal"&gt;localectl &lt;span class="pre"&gt;list-x11-keymap-models&lt;/span&gt;&lt;/tt&gt;&lt;/dd&gt;
&lt;dt&gt;layout:&lt;/dt&gt;
&lt;dd&gt;&lt;tt class="docutils literal"&gt;localectl &lt;span class="pre"&gt;list-x11-keymap-layouts&lt;/span&gt;&lt;/tt&gt;&lt;/dd&gt;
&lt;dt&gt;variant:&lt;/dt&gt;
&lt;dd&gt;&lt;tt class="docutils literal"&gt;localectl &lt;span class="pre"&gt;list-x11-keymaps-variants&lt;/span&gt; [layout]&lt;/tt&gt;&lt;/dd&gt;
&lt;dt&gt;options:&lt;/dt&gt;
&lt;dd&gt;&lt;tt class="docutils literal"&gt;localectl &lt;span class="pre"&gt;list-x11-keymaps-option&lt;/span&gt;&lt;/tt&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="adding-personality"&gt;
&lt;h2&gt;Adding&amp;nbsp;personality&lt;/h2&gt;
&lt;p&gt;Next we need to create, and customize, the initial user. By default,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; will create a user named &lt;tt class="docutils literal"&gt;ubuntu&lt;/tt&gt; with a locked password,
which also has password-less &lt;tt class="docutils literal"&gt;sudo&lt;/tt&gt; access, and a bunch of default group
memberships (for things like direct video and audio access, etc). The default
group memberships are fine, but I&amp;#8217;d like a slightly more personalized username,
and one with password-required &lt;tt class="docutils literal"&gt;sudo&lt;/tt&gt; rights. This can be accomplished with
the following&amp;nbsp;block:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;raspberry&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This changes the default username to &amp;#8220;dave&amp;#8221;, and uses my name in the &lt;tt class="docutils literal"&gt;gecos&lt;/tt&gt;
field (which traditionally recorded a whole bunch of things like display name,
office number, telephone number, and so forth but these days tends to just be
used for the display name). The &lt;tt class="docutils literal"&gt;sudo&lt;/tt&gt; setting specifically lacks the
&lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;NOPASSWD&lt;/span&gt;&lt;/tt&gt; flag, requiring a password for use. The &lt;tt class="docutils literal"&gt;ssh_import_id&lt;/tt&gt; setting
lists users from which to import public &lt;span class="caps"&gt;SSH&lt;/span&gt; keys (use &amp;#8220;lp:&amp;#8221; prefix for
Launchpad usernames, and &amp;#8220;gh:&amp;#8221; for&amp;nbsp;Github).&lt;/p&gt;
&lt;p&gt;The block also sets the initial password to the plain-text string &amp;#8220;raspberry&amp;#8221;
and ensures password login is possible (&lt;tt class="docutils literal"&gt;lock_passwd&lt;/tt&gt; is true by default
which means the only way to login is with &lt;span class="caps"&gt;SSH&lt;/span&gt; or another auxiliary mechanism,
but that&amp;#8217;s not so useful on the desktop!). This is horribly insecure! We could
do something &lt;em&gt;slightly&lt;/em&gt; more secure here by providing a hashed password
instead. First we&amp;#8217;d run &lt;tt class="docutils literal"&gt;mkpasswd&lt;/tt&gt; to generate the&amp;nbsp;hash:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mkpasswd&lt;span class="w"&gt; &lt;/span&gt;--method&lt;span class="o"&gt;=&lt;/span&gt;SHA-512&lt;span class="w"&gt; &lt;/span&gt;--rounds&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Password:
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;$rounds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="nv"&gt;$JZRfK1tM&lt;/span&gt;.xiWZtR5&lt;span class="nv"&gt;$XpMvuj2reJr&lt;/span&gt;.....lI0T4Z/
&lt;/pre&gt;
&lt;p&gt;Then we would use this with &lt;tt class="docutils literal"&gt;hashed_passwd&lt;/tt&gt; instead of
&lt;tt class="docutils literal"&gt;plain_text_passwd&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;hashed_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$6$rounds=4096$JZRfK1tM.xiWZtR5$XpMvuj2reJr.....lI0T4Z/&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;However, don&amp;#8217;t be lulled into a false sense of security. As the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt;
documentation &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest/reference/modules.html#users-and-groups"&gt;notes&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
While &lt;tt class="docutils literal"&gt;hashed_password&lt;/tt&gt; is better than &lt;tt class="docutils literal"&gt;plain_text_passwd&lt;/tt&gt;, using [a
password] in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; represents a security risk as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt;
could be accessible by third-parties depending on your cloud platform&lt;/blockquote&gt;
&lt;p&gt;To be clear: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; is a world-readable file on the Ubuntu Pi images (it
has to be because it&amp;#8217;s on a &lt;span class="caps"&gt;FAT&lt;/span&gt; partition). There is &lt;em&gt;no&lt;/em&gt; absolutely secure
method of setting the password via &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; (at least on the Ubuntu Pi
images); the secure thing to do here is login and then &lt;em&gt;change your password&lt;/em&gt;.
Consider this password initial and &lt;em&gt;temporary&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="baking-in-hacks"&gt;
&lt;h2&gt;Baking in&amp;nbsp;hacks&lt;/h2&gt;
&lt;p&gt;Finishing off our &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; file we&amp;#8217;re going to make one final rather hacky
(but useful) change. At the time of writing, Firefox starts under the XWayland
layer. This leads to rather poor performance under certain circumstances. To
force it to load under Wayland directly, we&amp;#8217;d need to set the
&lt;tt class="docutils literal"&gt;MOZ_ENABLE_WAYLAND&lt;/tt&gt; environment variable. Ideally we would do this only in
our user&amp;#8217;s environment, not system wide. This could be done by appending a line
to our user&amp;#8217;s &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.profile&lt;/span&gt;&lt;/tt&gt; script. However, this doesn&amp;#8217;t exist initially (and
won&amp;#8217;t until &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; creates our user). Thankfully, this presents no&amp;nbsp;problem!&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The powerful (but obviously slightly dangerous) &lt;tt class="docutils literal"&gt;write_files&lt;/tt&gt; key allows us
to write to arbitrary files on first boot. Here we specify we&amp;#8217;d like to write
to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/home/dave/.profile&lt;/span&gt;&lt;/tt&gt; (adjust for your custom username as necessary). We
also inform &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; that the write should be &lt;em&gt;deferred&lt;/em&gt; which means this
will take place &lt;em&gt;after&lt;/em&gt; user creation. Further, we specify that we wish to
append (not overwrite), and finally we provide the lines we&amp;#8217;d like to&amp;nbsp;append.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="going-shopping"&gt;
&lt;h2&gt;Going&amp;nbsp;shopping&lt;/h2&gt;
&lt;p&gt;Naturally, in order to obtain the packages we&amp;#8217;ve requested we&amp;#8217;re going to need
some network connectivity. If your Pi is going to be connected by Ethernet,
you&amp;#8217;re good to go; the default configuration will just use your Ethernet
connection. However, if you&amp;#8217;re going to be relying on WiFi instead we need to
be a bit of surgery in the last file we mentioned earlier, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;network-config&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Firstly we&amp;#8217;ll delete the &lt;tt class="docutils literal"&gt;ethernets&lt;/tt&gt; section, then uncomment the &lt;tt class="docutils literal"&gt;wifis&lt;/tt&gt;
section and specify the local access point and&amp;nbsp;password:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;wifis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;wlan0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;access-points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;my-wifi-ssid&amp;quot;&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;my-wifi-password&amp;quot;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;It is also useful to specify the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;regulatory-domain&lt;/span&gt;&lt;/tt&gt; here (note that this
needs to be specified under the &lt;tt class="docutils literal"&gt;wlan0&lt;/tt&gt; key, which is the reason for the
inclusion of the parent keys&amp;nbsp;below):&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;wifis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;wlan0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;regulatory-domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;GB&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The valid regulatory domains can be found in the &lt;a class="reference external" href="https://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git/tree/db.txt"&gt;Linux kernel source&lt;/a&gt;
Specifically, the two-letter code after the &amp;#8220;country&amp;#8221; line, which are almost
entirely the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2"&gt;&lt;span class="caps"&gt;ISO&lt;/span&gt;-3166 standard two-letter country codes&lt;/a&gt;. The special &amp;#8220;00&amp;#8221;
domain (the &amp;#8220;world regulatory domain&amp;#8221;) is the default, but relying on this can
lead to poor WiFi performance, particularly in 5GHz setups where many channels
have restrictions in the world&amp;nbsp;domain.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-first-byte-is-with-the-eye"&gt;
&lt;span id="down-here"&gt;&lt;/span&gt;&lt;h2&gt;The first byte is with the&amp;nbsp;eye&lt;/h2&gt;
&lt;p&gt;At the end of all this tinkering, you should have files that look something
like the following on your boot partition (lines you may wish to pay specific
attention to are&amp;nbsp;highlighted):&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="na"&gt;arm_boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c1"&gt;# Enable the audio output, I2C and SPI interfaces on the GPIO header. As these&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c1"&gt;# parameters related to the base device-tree they must appear *before* any&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;# other dtoverlay= specification&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;audio=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i2c_arm=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;spi=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c1"&gt;# Comment out the following line if the edges of the desktop appear outside&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="c1"&gt;# the edges of your display&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="na"&gt;disable_overscan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="c1"&gt;# If you have issues with audio, you may try uncommenting the following line&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="c1"&gt;# which forces the HDMI output into HDMI mode instead of DVI (which doesn&amp;#39;t&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# support audio output)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;#hdmi_drive=2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="c1"&gt;# Enable the serial pins&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="c1"&gt;#enable_uart=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="c1"&gt;# Autoload overlays for any recognized cameras or displays that are attached&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="c1"&gt;# to the CSI/DSI ports. Please note this is for libcamera support, *not* for&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="c1"&gt;# the legacy camera stack&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="na"&gt;camera_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="na"&gt;display_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="c1"&gt;# Config settings specific to arm64&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="k"&gt;[cm4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="c1"&gt;# Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="c1"&gt;# such a board)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2,dr_mode=host&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;cmdline.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Please note that &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; must consist of a &lt;em&gt;single line of text&lt;/em&gt;.
Ignore any wrapping of text&amp;nbsp;above.&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lubuntu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nt"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;en_GB.UTF-8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nt"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Europe/London&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="nt"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pc105&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ctrl:nocaps&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;dave&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;lock_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Dave&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jones&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;plain_text_passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;raspberry&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ALL=(ALL:ALL)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/dave/.profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;append&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;export MOZ_ENABLE_WAYLAND=1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="nt"&gt;package_reboot_if_required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;lubuntu-desktop&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ubuntu-raspi-settings-desktop&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="nt"&gt;snap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;snap install firefox&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You may be wondering if the ordering of these blocks matters. The answer is
&amp;#8220;no&amp;#8221;. The ordering of actions in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; does &lt;em&gt;not&lt;/em&gt; depend on the
ordering of the configuration file (and nor should it). The system is intended
to be &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Declarative_programming"&gt;declarative&lt;/a&gt;; we are describing the state we wish to attain, not how to
obtain&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;This is why the &lt;tt class="docutils literal"&gt;write_files&lt;/tt&gt; section has options like &lt;tt class="docutils literal"&gt;defer&lt;/tt&gt; which adjust
the timing of these actions. By default, &lt;tt class="docutils literal"&gt;write_files&lt;/tt&gt; entries will always
occur early on so that written files can be available to other&amp;nbsp;actions.&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;network-config&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="nt"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;wifis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;wlan0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;regulatory-domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;GB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;access-points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;my-wifi-ssid&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;my-wifi-password&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With all those written, safely eject your card (or &lt;span class="caps"&gt;SSD&lt;/span&gt; drive), plug it into
your Pi and let it boot. This will take a … very … very … long … time.
Installing an entire hierarchy of desktop packages in this manner is not a
quick process, and the first boot will probably take at least 1 hour (I didn&amp;#8217;t
time every installation during this test, but the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; logs for the
Lubuntu run indicated setup on the microSD card took roughly 1½&amp;nbsp;hours).&lt;/p&gt;
&lt;p&gt;However, once it&amp;#8217;s complete, it should automatically reboot and you should find
yourself at a shiny new graphical desktop login! There&amp;#8217;s still some horror to
sort out here (in particular the clash of networking configuration stacks), but
we&amp;#8217;ll deal with that next time when we&amp;#8217;ll also take a look at comparing the
current crop of desktops on Ubuntu, and see what&amp;#8217;s changed in the last few
years. We&amp;#8217;ll also compare them to the official Gnome desktop, and learn a few
more &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt; tricks&amp;nbsp;…&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="linux-mount" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;If you&amp;#8217;re using Linux, you&amp;#8217;ll also see the root partition
(&amp;#8220;writable&amp;#8221;) appear, but you can ignore this&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="oem-config" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;oem-config&lt;/span&gt;&lt;/tt&gt;, derived from the &lt;tt class="docutils literal"&gt;ubiquity&lt;/tt&gt; installer. There
are moves afoot to replace this at some point (given &lt;tt class="docutils literal"&gt;ubiquity&lt;/tt&gt; is being
replaced with &lt;tt class="docutils literal"&gt;subiquity&lt;/tt&gt;), but nothing concrete as yet&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="no-systemd" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p class="first"&gt;For those violently allergic to systemd, the equivalent commands the
listing locales, timezones, and keyboard settings&amp;nbsp;are:&lt;/p&gt;
&lt;dl class="last docutils"&gt;
&lt;dt&gt;locale:&lt;/dt&gt;
&lt;dd&gt;&lt;tt class="docutils literal"&gt;locale &lt;span class="pre"&gt;-a&lt;/span&gt;&lt;/tt&gt;&lt;/dd&gt;
&lt;dt&gt;timezone:&lt;/dt&gt;
&lt;dd&gt;&lt;tt class="docutils literal"&gt;(cd /usr/share/zoneinfo/posix; find &lt;span class="pre"&gt;-type&lt;/span&gt; f &lt;span class="pre"&gt;-or&lt;/span&gt; &lt;span class="pre"&gt;-type&lt;/span&gt; l &lt;span class="pre"&gt;-printf&lt;/span&gt; '%P\n')&lt;/tt&gt;&lt;/dd&gt;
&lt;dt&gt;keyboard:&lt;/dt&gt;
&lt;dd&gt;Read various sections in &lt;tt class="docutils literal"&gt;/usr/share/X11/xkb/rules/base.lst&lt;/tt&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="cloud-init"></category><category term="desktop"></category></entry><entry><title>4K 60Hz on a Pi</title><link href="https://waldorf.waveform.org.uk/2023/4k-60hz-on-a-pi.html" rel="alternate"></link><published>2023-06-23T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-06-23:/2023/4k-60hz-on-a-pi.html</id><summary type="html">&lt;p class="first last"&gt;In which Dave attempts to read text on a high-&lt;span class="caps"&gt;DPI&lt;/span&gt; display, fails, and
solves the problem in the most basic way&amp;nbsp;possible&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;&lt;/strong&gt;: this is a long rambling semi-rant of the sort I occasionally go off
on. If you just want to get 4k 60Hz working under Ubuntu on the Pi, skip &lt;a class="reference internal" href="#down-here"&gt;down
here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Your humble correspondent is, dear reader, something of a luddite when it comes
to display technology. On my desk is a (by modern standards) positively
pedestrian 1080p monitor attached to my &lt;span class="caps"&gt;PC&lt;/span&gt;, and another one attached to my main
development Pi. Not for me, a high-&lt;span class="caps"&gt;DPI&lt;/span&gt; dual- or triple-monitor&amp;nbsp;setup!&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve never found a particular need for multiple monitors on a machine and,
every time I&amp;#8217;ve tried it, the &lt;span class="caps"&gt;OS&lt;/span&gt; in question (whether Linux or Windows) has
found some novel way to screw things up, whether it be with full-screen
applications, moving things between monitors, or even positioning a centered
dialog&amp;nbsp;box.&lt;/p&gt;
&lt;p&gt;The more recent advances in high-&lt;span class="caps"&gt;DPI&lt;/span&gt; displays have also done little to enthuse
me. My little laptop with its 11.6&amp;#8221; screen (no, I &lt;em&gt;don&amp;#8217;t&lt;/em&gt; want a huge 15&amp;#8221;+
luggable with a numeric keypad, it&amp;#8217;s meant to be &lt;em&gt;portable&lt;/em&gt;!) has a high-&lt;span class="caps"&gt;DPI&lt;/span&gt;
1080p screen (~190dpi). As a result, the curves and lines of fonts on the
screen are beautifully smooth … and too small to be legible to my aging eyes
without cranking up the display scale &lt;a class="footnote-reference" href="#scaling" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. So when the Pi 4 was
released with &lt;em&gt;4K display capability!&lt;/em&gt; and &lt;em&gt;dual micro-&lt;span class="caps"&gt;HDMI&lt;/span&gt; ports!&lt;/em&gt; my interest
was not exactly piqued until I got to &lt;em&gt;dual &lt;span class="caps"&gt;USB3&lt;/span&gt; ports!&lt;/em&gt; and &lt;em&gt;more than &lt;span class="caps"&gt;1GB&lt;/span&gt; of
&lt;span class="caps"&gt;RAM&lt;/span&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;div class="section" id="linux-oleum"&gt;
&lt;h2&gt;Linux-oleum&lt;/h2&gt;
&lt;p&gt;Several people have commented, upon learning of my solo-screened predilections,
&amp;#8220;how do you cope with having to switch between applications? I need two screens
so I can be working on one while looking something up on the other.&amp;#8221; This is a
reasonable concern. I always need a couple of windows open: the terminal I&amp;#8217;m
working in, and the web-browser I&amp;#8217;m reading something from. But the answer has
existed for a good long time, and is available in every modern &lt;span class="caps"&gt;OS&lt;/span&gt; and windowing
system:&amp;nbsp;tiling.&lt;/p&gt;
&lt;img alt="Frugal re-use of an old picture of my desk, showing a vertically split desktop with my terminal on the left, and Firefox on the right. Cables, switches, wires, a spare keyboard, the odd breadboard, and a soldering iron are liberally scattered around the monitor." src="https://waldorf.waveform.org.uk/images/hairy-pi.jpg" /&gt;
&lt;p&gt;I stick my terminal on the left, my browser on the right and now, not only do I
enjoy two windows together on the screen, they both have a pleasingly
A4-paper-esque aspect ratio which is greatly preferable for reading
&lt;a class="footnote-reference" href="#width-limit" id="footnote-reference-2"&gt;[2]&lt;/a&gt;. Naturally, there are downsides. The sheer number of websites
which purport to have a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Responsive_web_design"&gt;responsive&lt;/a&gt; design but casually fall off the right of
the screen is infuriating &lt;a class="footnote-reference" href="#crap-width" id="footnote-reference-3"&gt;[3]&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-unbearable-lightness-of-daytime-tv"&gt;
&lt;h2&gt;The Unbearable Lightness of Daytime &lt;span class="caps"&gt;TV&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Thus I remain firmly unconvinced by the benefits of having multiple monitors.
My friend Ben did, however, come up with a setup which I confess I would
merrily steal were it not for a distinct lack of space on my desk … buy a &lt;span class="caps"&gt;TV&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Now, I don&amp;#8217;t just mean any mere kitchen-suitable model, barely capable of the
1080p of yesteryear. No! A full-throated, red-blooded, 40 diagonal inches of
&lt;em&gt;ultra&lt;/em&gt; &lt;span class="caps"&gt;HD&lt;/span&gt; 4K-ready &lt;span class="caps"&gt;LED&lt;/span&gt; &lt;span class="caps"&gt;TV&lt;/span&gt;, ready to blast screaming rainbows at your reluctant&amp;nbsp;retinas!&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="Ben's obscenely large &amp;quot;monitor&amp;quot; looms over his suspiciously tidy desk, displaying a (comparatively) tiny game of wordle in the middle of a maximized browser window. For the terminally curious, LIVER is the solution, which Ben has guessed in three attempts." src="https://waldorf.waveform.org.uk/images/bens-monitor.jpg" /&gt;
&lt;p class="caption"&gt;Ben swears this is a &amp;#8220;monitor&amp;#8221; &lt;a class="footnote-reference" href="#with-remote" id="footnote-reference-4"&gt;[4]&lt;/a&gt;. I can only apologise for
the lack of screaming rainbows&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This makes use of 4K display outputs without producing text that requires
squinting through a loupe to read. Furthermore, it doesn&amp;#8217;t cost an arm and a
leg like most multi-monitor setups (my frugality in computing extends beyond
the mere&amp;nbsp;computer!).&lt;/p&gt;
&lt;p&gt;It sounds expensive, and if you&amp;#8217;re purchasing a 40&amp;#8221; monitor … it really is.
You&amp;#8217;ll be hard pushed to find anything at the 40&amp;#8221; scale in monitors for less
than 700 quid. If you &lt;em&gt;do&lt;/em&gt; find something for less than that … you&amp;#8217;re likely to
find it&amp;#8217;s actually just a &lt;span class="caps"&gt;TV&lt;/span&gt; being sold as a monitor &lt;a class="footnote-reference" href="#another-remote" id="footnote-reference-5"&gt;[5]&lt;/a&gt;. TVs,
on the other hand, can be found at this scale for well under 400 of your finest
pounds&amp;nbsp;sterling.&lt;/p&gt;
&lt;p&gt;Why the price difference? Surely TVs do a lot more than a monitor? They&amp;#8217;ve all
got speakers (unlike many monitors), they&amp;#8217;ve all got tuners, and these days at
this scale they&amp;#8217;ll all have some &amp;#8220;smart&amp;#8221; functionality (i.e. some rubbish menu
system to run their &amp;#8220;apps&amp;#8221;), not to mention some &lt;span class="caps"&gt;IR&lt;/span&gt; / Bluetooth remote thingy.
Is it all a gigantic con? Slap &amp;#8220;monitor&amp;#8221; instead of &amp;#8220;&lt;span class="caps"&gt;TV&lt;/span&gt;&amp;#8221; on a screen and you
can instantly sell it for twice the&amp;nbsp;price?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-need-for-speed"&gt;
&lt;h2&gt;The Need for&amp;nbsp;Speed&lt;/h2&gt;
&lt;p&gt;Monitors at this scale all boast of their response time, and it&amp;#8217;s usually
something under 10 milliseconds. From the signal coming out of your computer,
to it being beamed to your eager eyeballs, is typically less than one frame&amp;#8217;s
duration at a 60Hz refresh rate. But for a &lt;span class="caps"&gt;TV&lt;/span&gt; it doesn&amp;#8217;t matter at all if it
takes one (or more) frame&amp;#8217;s time between Disney+ pumping a signal down your
broadband connection, to The Bob&amp;#8217;s Burgers Movie infesting your childrens&amp;#8217;&amp;nbsp;brains.&lt;/p&gt;
&lt;p&gt;This (allegedly) matters for&amp;nbsp;gamers.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="Bob Belcher and his family stare slack-jawed out of the screen." src="https://waldorf.waveform.org.uk/images/bobs-burgers.png" /&gt;
&lt;p class="caption"&gt;Bob and his family look at the price of &lt;a class="reference external" href="https://www.amazon.co.uk/Corsair-Xeneon-45WQHD240-Gaming-Monitor/dp/B0BS1J4RKD/"&gt;&amp;#8220;gaming&amp;#8221; monitor&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Being a gamer myself (though not the sort of twitchy adrenaline junkie who runs
around pwning people in CoD … wandering around admiring the stark-yet-beautiful
scenery in Fallout is more my speed), I suppose it &lt;em&gt;ought&lt;/em&gt; to matter to me but
I can&amp;#8217;t say I&amp;#8217;ve ever given it much thought. Then again, I&amp;#8217;ve always used a
&lt;em&gt;monitor&lt;/em&gt; and not a &lt;span class="caps"&gt;TV&lt;/span&gt; so perhaps I&amp;#8217;ve never had a display where it&amp;nbsp;mattered?&lt;/p&gt;
&lt;p&gt;But, hold fire! What about these &amp;#8220;console&amp;#8221; things? I&amp;#8217;m reliably informed these
are even more popular than a &lt;span class="caps"&gt;PC&lt;/span&gt; for gaming &lt;a class="footnote-reference" href="#wot-no-mouse" id="footnote-reference-6"&gt;[6]&lt;/a&gt;, yet … they
typically plug into a &lt;span class="caps"&gt;TV&lt;/span&gt;. Are the inputs of console gamers all stuck several
frames in the past? The lag ruining their reactions? Are they unable to compete
with the might of the &lt;span class="caps"&gt;PC&lt;/span&gt;&amp;nbsp;Gamer?&lt;/p&gt;
&lt;p&gt;Erm … no. Or if they are, I certainly can&amp;#8217;t notice it on my daughter&amp;#8217;s Switch
running Minecraft on the &lt;span class="caps"&gt;TV&lt;/span&gt; downstairs. Having said that, we&amp;#8217;ve already
established the acuity of my visual system is far from perfect. Still, the
point stands. For my purposes (and thus presumably a few others): yes, it&amp;#8217;s all
a giant con and one can get a screen of twice the size for half the price
simply by searching for &amp;#8220;&lt;span class="caps"&gt;TV&lt;/span&gt;&amp;#8221; instead of &amp;#8220;monitor&amp;#8221; when&amp;nbsp;shopping.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="this-ll-hertz-me-more"&gt;
&lt;span id="down-here"&gt;&lt;/span&gt;&lt;h2&gt;This&amp;#8217;ll Hertz Me More&amp;nbsp;…&lt;/h2&gt;
&lt;p&gt;The Pi 4 is capable of running 4K at 60Hz, but it won&amp;#8217;t do it out of the box,
and it requires some gentle cajoling to get it in the mood for 60Hz operation.
The reason for this is simple: 4K at 60Hz is pushing the absolute limits of the
Pi 4&amp;#8217;s display hardware and as a result there are limitations when using&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Firstly, ensure you have an adequate cable. Specifically, you need an &lt;span class="caps"&gt;HDMI&lt;/span&gt; 2.0
or later cable in order to support 4K at 60Hz. Confusingly, &lt;span class="caps"&gt;HDMI&lt;/span&gt; 1.4 cables
support the 1080p resolution at 60Hz, and the 4K resolution but only up to
30Hz. You need &lt;span class="caps"&gt;HDMI&lt;/span&gt; 2.0 (or later) cabling to support the 4K resolution at
60Hz. If, like me, you&amp;#8217;ve been stuck in 1080p land for a while you may have
amassed a small hoard of spare &lt;span class="caps"&gt;HDMI&lt;/span&gt; cables, only to find that none of them
support &lt;span class="caps"&gt;HDMI&lt;/span&gt; 2.0.&amp;nbsp;Sorry!&lt;/p&gt;
&lt;p&gt;Next, use the &lt;span class="caps"&gt;HDMI&lt;/span&gt;-0 port on the Pi; the &lt;span class="caps"&gt;HDMI&lt;/span&gt;-1 port cannot be used when
operating at 4K 60Hz. The &lt;span class="caps"&gt;HDMI&lt;/span&gt;-0 port is nearest the &lt;span class="caps"&gt;USB&lt;/span&gt;-C power port on the Pi
4, and nearest the micro-&lt;span class="caps"&gt;SD&lt;/span&gt; card slot on the Pi&amp;nbsp;400.&lt;/p&gt;
&lt;p&gt;Now it&amp;#8217;s time for some configuration changes. Firstly, add the following line
to &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; on the boot partition &lt;a class="footnote-reference" href="#wheres-boot" id="footnote-reference-7"&gt;[7]&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
hdmi_enable_4kp60=1
&lt;/pre&gt;
&lt;p&gt;The line should appear under an &lt;tt class="docutils literal"&gt;[all]&lt;/tt&gt; section (or I suppose more correctly
a &lt;tt class="docutils literal"&gt;[pi4]&lt;/tt&gt; section which would mean it only applies to a Pi 4, 400, &lt;span class="caps"&gt;CM4&lt;/span&gt;, or
&lt;span class="caps"&gt;CM4S&lt;/span&gt; &amp;#8212; however, it won&amp;#8217;t cause any issues on older models as the bootloader
will simply ignore the line on&amp;nbsp;those).&lt;/p&gt;
&lt;p&gt;Finally, reboot your Pi to apply these changes. When you&amp;#8217;re back to your
desktop you may note, if you are already in a 4K resolution, that you&amp;#8217;re still
operating at 30Hz (your mouse motion will not look smooth!). This is because
you still need to tell your desktop to switch to the higher refresh rate. In
Gnome, this is as simple as going to Settings, selecting &amp;#8220;Screen Display&amp;#8221; on
the left, and then picking &amp;#8220;60Hz&amp;#8221; under the &amp;#8220;Refresh Rate&amp;#8221;&amp;nbsp;option.&lt;/p&gt;
&lt;p&gt;After your screen does its mode-switching dance, you should have buttery smooth
mouse&amp;nbsp;motion!&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="An image of Dave's hotel room at an engineering sprint. Amidst a collection of socks, a Pi 400 is hooked up to the huge 40-something inch TV in front of the bed. The screen is displaying the Ubuntu logo typically seen during desktop boot. In the left foreground, two toolboxes are open on the bed. On the right, a small table is covered in the detritus of the day: Dave's laptop, empty coffee mug, glasses, a copy of Private Eye, a graphic novel, and several random bits of Pi paraphenalia." src="https://waldorf.waveform.org.uk/images/hotel-4k.jpg" /&gt;
&lt;p class="caption"&gt;No hotel &lt;span class="caps"&gt;TV&lt;/span&gt; is safe&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Be warned that 4K at 60Hz is not exactly a paradise on the Pi. This resolution
requires significantly more memory for many things and, in particular, I&amp;#8217;ve
found that several things cannot operate full-screen. Notably, Firefox crashed
quite painfully when I was attempting to test it playing a video full-screen.
However, when Firefox was forced to run purely under Wayland
&lt;a class="footnote-reference" href="#moz-enable-wayland" id="footnote-reference-8"&gt;[8]&lt;/a&gt; (as opposed to the XWayland server it defaults to
currently), it &lt;em&gt;did&lt;/em&gt; succeed in working (not smoothly, but at least it didn&amp;#8217;t
burst into&amp;nbsp;flames).&lt;/p&gt;
&lt;p&gt;The Pi will also run hotter and demand more power at this resolution and
framerate. If you experience crashes, double-check you have a &lt;em&gt;good&lt;/em&gt; power
supply that isn&amp;#8217;t dropping voltage at higher loads (even if you&amp;#8217;ve never had
problems before, that&amp;#8217;s no guarantee you won&amp;#8217;t with this&amp;nbsp;configuration).&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s all for this time! Next, I&amp;#8217;ll be having a look at spinning up a custom
desktop from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="scaling" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;thank goodness Wayland makes fractional scaling on Gnome &lt;em&gt;fast&lt;/em&gt;;
200% was too big, but 150% is perfect&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="width-limit" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;have you noticed how many websites limit their horizontal
width? There&amp;#8217;s a reason for this …&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="crap-width" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;GitHub recently fixed this after being broken for nearly a
decade, meanwhile Google now frequently falls off the right after spending
the last decade being perfect … oh well, you win some, you lose some&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="with-remote" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;… with a remote&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="another-remote" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Oh look, it&amp;#8217;s a &lt;a class="reference external" href="https://www.amazon.co.uk/Samsung-LS43BM700UUXXU-Smart-Monitor-streaming/dp/B09VH6M7P6/"&gt;&amp;#8220;monitor&amp;#8221; with … a remote for Netflix,
Disney+ and Prime Video&lt;/a&gt;.
Amazon, you really are the new flea-bay&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="wot-no-mouse" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;How this came to be when they lack even the most basic sort
of mouse I have no idea&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="wheres-boot" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-7"&gt;[7]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This is typically mounted on &lt;tt class="docutils literal"&gt;/boot/firmware&lt;/tt&gt; on Ubuntu, or
&lt;tt class="docutils literal"&gt;/boot&lt;/tt&gt; under RaspiOS&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="moz-enable-wayland" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-8"&gt;[8]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This can be done by adding &lt;tt class="docutils literal"&gt;export
MOZ_ENABLE_WAYLAND=1&lt;/tt&gt; to your &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.profile&lt;/span&gt;&lt;/tt&gt; script and re-logging in.
Hopefully this mode will become the default in the near future, as Firefox
operates visibly more smoothly under pure Wayland&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="ubuntu"></category><category term="4k"></category><category term="lunar"></category></entry><entry><title>Playing with Blocks - ReLUKS</title><link href="https://waldorf.waveform.org.uk/2023/playing-with-blocks-reluks.html" rel="alternate"></link><published>2023-04-20T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-04-20:/2023/playing-with-blocks-reluks.html</id><summary type="html">&lt;p class="first last"&gt;Making a Pi desktop with encrypted root storage&amp;nbsp;(again!)&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/a&gt;,
&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2022/playing-with-blocks-its-all-connected.html"&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;+&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;ve looked at using &lt;span class="caps"&gt;LUKS&lt;/span&gt; on the Ubuntu Pi desktop images &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;before&lt;/a&gt; but it was a rather painful
process primarily as it involved shrinking the root partition. This in turn
meant that a separate machine (or at the very least a Pi booting from something
other than its &lt;span class="caps"&gt;SD&lt;/span&gt; card) was required to perform the&amp;nbsp;changes.&lt;/p&gt;
&lt;p&gt;Can we do better? In particular, can we reduce the requirements to just a
single machine (the target Raspberry Pi) with a single storage medium? Thanks
to a relatively minor change in the just-released Ubuntu Lunar (23.04) Pi
desktop image, I think we&amp;nbsp;can!&lt;/p&gt;
&lt;div class="section" id="when-you-wanna-go-do-it"&gt;
&lt;h2&gt;When you wanna go do&amp;nbsp;it&lt;/h2&gt;
&lt;p&gt;You&amp;#8217;ll need the following for any&amp;nbsp;scenario:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;a Raspberry Pi 4 or&amp;nbsp;400&lt;/li&gt;
&lt;li&gt;a micro-&lt;span class="caps"&gt;SD&lt;/span&gt; card (or &lt;span class="caps"&gt;USB&lt;/span&gt;-attached boot&amp;nbsp;drive)&lt;/li&gt;
&lt;li&gt;a&amp;nbsp;monitor&lt;/li&gt;
&lt;li&gt;a keyboard and a&amp;nbsp;mouse&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;#8217;re trying to do this on a single machine (not flashing your storage
device on a separate machine), you&amp;#8217;ll also&amp;nbsp;need:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;a relatively recent boot&amp;nbsp;firmware&lt;/li&gt;
&lt;li&gt;an Ethernet&amp;nbsp;connection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Newly purchased Pis (as of 2023) should all have sufficiently up to date
firmwares. Older Pis may require an &lt;span class="caps"&gt;EEPROM&lt;/span&gt; update which can be accomplished
under Ubuntu (or RaspiOS) by running &lt;tt class="docutils literal"&gt;sudo &lt;span class="pre"&gt;rpi-eeprom-update&lt;/span&gt; &lt;span class="pre"&gt;-a&lt;/span&gt;&lt;/tt&gt; and
rebooting if an update is shown as&amp;nbsp;available.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="hit-me-with-those-laser-beams"&gt;
&lt;h2&gt;Hit me with those laser&amp;nbsp;beams&lt;/h2&gt;
&lt;p&gt;Start up your Raspberry Pi. The monitor should show the red and white Raspberry
Pi bootloader screen, with the prompt &amp;#8220;Press and hold &amp;lt;&lt;span class="caps"&gt;SHIFT&lt;/span&gt;&amp;gt; key to stop boot
and start net install&amp;#8221;. Follow this instruction and it should change to &amp;#8220;If not
done so already, insert ethernet cable&amp;#8221; (this capability only works over
Ethernet, not WiFi as the bootloader can only configure the&amp;nbsp;former).&lt;/p&gt;
&lt;p&gt;After a short download, you should find yourself at a full-screen version of
the &lt;a class="reference external" href="https://snapcraft.io/rpi-imager"&gt;rpi-imager&lt;/a&gt;. From here you can select Ubuntu as you normally would in the
desktop version of the&amp;nbsp;software:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Click &amp;#8220;Choose &lt;span class="caps"&gt;OS&lt;/span&gt;&amp;#8221;&lt;/li&gt;
&lt;li&gt;Select &amp;#8220;Other general-purpose &lt;span class="caps"&gt;OS&lt;/span&gt;&amp;#8221;&lt;/li&gt;
&lt;li&gt;Select&amp;nbsp;&amp;#8220;Ubuntu&amp;#8221;&lt;/li&gt;
&lt;li&gt;Select &amp;#8220;Ubuntu Desktop 23.04&amp;nbsp;(64-bit)&amp;#8221;&lt;/li&gt;
&lt;li&gt;Back at the main screen, click &amp;#8220;Choose Storage&amp;#8221; and pick your boot medium
(which should be the only drive connected to the Pi at this&amp;nbsp;point)&lt;/li&gt;
&lt;li&gt;Click&amp;nbsp;&amp;#8220;Write&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The utility should prompt to ensure you want to overwrite everything and
anything on the storage device and, once confirmed, you should find it writing
to the storage&amp;nbsp;medium.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Do not be alarmed if there is an extremely long pause in the writing
process. The Ubuntu images are heavily compressed, and the &amp;#8220;slack space&amp;#8221;
baked into the image (which &lt;a class="reference external" href="https://github.com/raspberrypi/rpi-imager/issues/480"&gt;may be excessive&lt;/a&gt;) compresses &lt;em&gt;extremely&lt;/em&gt;
well. This can lead to a potentially long pause in the progress bar while a
few bytes in the compressed image expand into gigabytes of written&amp;nbsp;data.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="make-making-it-your-intention"&gt;
&lt;h2&gt;Make making it your&amp;nbsp;intention&lt;/h2&gt;
&lt;p&gt;Once the &lt;span class="caps"&gt;OS&lt;/span&gt; is written to the boot medium (and verified), the Pi should reboot
into Ubuntu Desktop. You&amp;#8217;ll need to run through the initial setup wizard,
configuring your locale and user&amp;nbsp;details.&lt;/p&gt;
&lt;p&gt;After the initial setup wizard, login to your new user. We now need to take a
series of steps which may seem premature and in some cases a bit odd. Start a
terminal and perform the&amp;nbsp;following:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;cryptsetup-initramfs&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rootfs  &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;findmnt&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;SOURCE&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;  none  luks,discard&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s,^LABEL=writable,/dev/mapper/rootfs,'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s,LABEL=writable,/dev/mapper/rootfs,'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;reboot
&lt;/pre&gt;
&lt;p&gt;In order, these&amp;nbsp;commands:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Switch to the root user (everything we&amp;#8217;re going to do requires root access
and I can&amp;#8217;t be bothered re-typing&amp;nbsp;sudo).&lt;/li&gt;
&lt;li&gt;Re-install &lt;tt class="docutils literal"&gt;cryptsetup&lt;/tt&gt;. I say &amp;#8220;re-&amp;#8221; because this package is actually
part of the base image, but the initial setup decides it isn&amp;#8217;t required and
removes it at the end of the setup.&amp;nbsp;Doh!&lt;/li&gt;
&lt;li&gt;Create the &lt;tt class="docutils literal"&gt;crypttab&lt;/tt&gt; entry for the&amp;nbsp;rootfs.&lt;/li&gt;
&lt;li&gt;Re-write the source of the root mount in &lt;tt class="docutils literal"&gt;fstab&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;… and the same in the kernel command&amp;nbsp;line.&lt;/li&gt;
&lt;li&gt;Reboot the&amp;nbsp;machine.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to double check things, assuming your boot device is an &lt;span class="caps"&gt;SD&lt;/span&gt; card
(&lt;tt class="docutils literal"&gt;/dev/mmcblk0&lt;/tt&gt;) your &lt;tt class="docutils literal"&gt;crypttab&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;fstab&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; files
should look like&amp;nbsp;this:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;root&amp;#64;miss-piggy:~# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&amp;lt;target&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;device&amp;gt;&lt;span class="w"&gt;         &lt;/span&gt;&amp;lt;key&lt;span class="w"&gt; &lt;/span&gt;file&amp;gt;&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;options&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;rootfs  /dev/mmcblk0p2  none  luks,discard
&lt;/span&gt;&lt;span class="gp"&gt;root&amp;#64;miss-piggy:~# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;/dev/mapper/rootfs      /       ext4    discard 0       1
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
&lt;/span&gt;&lt;span class="gp"&gt;root&amp;#64;miss-piggy:~# &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;zswap.enabled=1 zswap.zpool=z3fold zswap.compressor=zstd dwc_otg.lpm_enable=0 console=tty1 root=/dev/mapper/rootfs rootfstype=ext4 rootwait fixrtc quiet splash&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="hit-me-hit-me"&gt;
&lt;h2&gt;Hit me, hit&amp;nbsp;me!&lt;/h2&gt;
&lt;p&gt;At this point you may notice that the boot seems to be taking rather a long
time. In fact, it&amp;#8217;s going to fail because the &lt;tt class="docutils literal"&gt;rootfs&lt;/tt&gt; device we told the
kernel to go looking for doesn&amp;#8217;t exist yet. But that&amp;#8217;s okay! Be patient and let
it fail; the initramfs still gets loaded and it&amp;#8217;s got all the tools we need to
create&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Once you find yourself at a command prompt (with a notice complaining that it
couldn&amp;#8217;t mount the root file-system), do the&amp;nbsp;following:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp-VirtualEnv"&gt;(initramfs)&lt;/span&gt; &lt;span class="go"&gt;rootdev=$(readlink -f /dev/disk/by-label/writable)
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(initramfs)&lt;/span&gt; &lt;span class="go"&gt;cryptsetup reencrypt --encrypt --reduce-device-size 16M --cipher xchacha12,aes-adiantum-plain64 $rootdev
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(initramfs)&lt;/span&gt; &lt;span class="go"&gt;cryptsetup open $rootdev rootfs
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(initramfs)&lt;/span&gt; &lt;span class="go"&gt;exit&lt;/span&gt;
&lt;/pre&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Create the rootfs device. This will prompt you to enter &amp;#8220;&lt;span class="caps"&gt;YES&lt;/span&gt;&amp;#8221; (for
confirmation you really want to do this), and for the password to unlock the
encryption key for the device (twice); &lt;em&gt;don&amp;#8217;t forget this!&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Go make coffee; or twiddle your thumbs for an excessively long period of time
while it encrypts the &lt;em&gt;entire&lt;/em&gt; device, including all the empty space. On the
plus side, after a minute it should give you a reasonably accurate estimate
of how long it&amp;#8217;s going to take to encrypt the device, so you can set a timer
and come back when it&amp;#8217;s&amp;nbsp;done.&lt;/li&gt;
&lt;li&gt;Open the newly created rootfs device (this will prompt you once more for the
password) and &lt;tt class="docutils literal"&gt;exit&lt;/tt&gt; to continue the&amp;nbsp;boot.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="re-luks"&gt;
&lt;h2&gt;Re-&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Once you&amp;#8217;re logged into the desktop once more, you have one final task.
Re-generate the initramfs now that the &lt;tt class="docutils literal"&gt;rootfs&lt;/tt&gt; device&amp;nbsp;exists:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;update-initramfs&lt;span class="w"&gt; &lt;/span&gt;-u
&lt;/pre&gt;
&lt;p&gt;Done! At this point all future boots should prompt you for the password to
unlock the encrypted root&amp;nbsp;file-system.&lt;/p&gt;
&lt;p&gt;Obviously in an ideal world, this could all be accomplished from the installer,
but there&amp;#8217;s a slight wrinkle in that with the lunar release. The first time
setup application on the Pi desktop images is &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;oem-config&lt;/span&gt;&lt;/tt&gt; which is derived
from the venerable &lt;tt class="docutils literal"&gt;ubiquity&lt;/tt&gt; installer. As you may have noticed in the
&lt;a class="reference external" href="https://discourse.ubuntu.com/t/lunar-lobster-release-notes/31910"&gt;lunar release notes&lt;/a&gt; the installer is being replaced by a new one based on
&lt;tt class="docutils literal"&gt;subiquity&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;However, this only applies to those platforms which need the &amp;#8220;full fat&amp;#8221;
installer, i.e. the &lt;span class="caps"&gt;PC&lt;/span&gt; where you boot off some medium and then install to
another (the internal drive). It &lt;em&gt;doesn&amp;#8217;t&lt;/em&gt; apply to Ubuntu&amp;#8217;s pre-installed
desktop images (like the Pi ones) yet. There seemed little point in pouring
effort into adding this to &lt;tt class="docutils literal"&gt;ubiquity&lt;/tt&gt; this cycle, only to see it thrown out
during the next cycle when the &lt;tt class="docutils literal"&gt;subiquity&lt;/tt&gt; based installer comes to replace
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;oem-config&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully in a future cycle we can integrate all this and have a slightly
friendlier experience, but for now at least it&amp;#8217;s achievable without requiring a
separate&amp;nbsp;machine.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="ubuntu"></category><category term="storage"></category><category term="luks"></category><category term="lunar"></category></entry><entry><title>Wherefore UART Thou?</title><link href="https://waldorf.waveform.org.uk/2022/wherefore-uart-thou.html" rel="alternate"></link><published>2022-10-21T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2022-10-21:/2022/wherefore-uart-thou.html</id><summary type="html">&lt;p class="first last"&gt;Methods of working with MicroPython, old and&amp;nbsp;new&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="https://micropython.readthedocs.io/"&gt;MicroPython&lt;/a&gt; is one of the things in modern computing that I still find
somewhat miraculous and slightly surreal. The idea that a &lt;em&gt;microcontroller&lt;/em&gt;,
i.e. the sort of processor I grew up using, could run something as heavyweight
as an interpreted language like Python, and could do it sufficiently fast as to
be genuinely useful still amazes me to some&amp;nbsp;degree.&lt;/p&gt;
&lt;p&gt;Of course, my judgment is faulty: the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/RP2040"&gt;&lt;span class="caps"&gt;RP2040&lt;/span&gt;&lt;/a&gt; is a positive behemoth compared
to the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Zilog_Z80"&gt;Z80&lt;/a&gt; that powered many of the machines of my youth. Suffice it to say, I
&lt;em&gt;love&lt;/em&gt; playing around with MicroPython on micro-controllers, particularly on
the Raspberry Pi &lt;a class="reference external" href="https://www.raspberrypi.com/products/raspberry-pi-pico/"&gt;Pico&lt;/a&gt;, and various boards using the Espressif &lt;a class="reference external" href="https://en.wikipedia.org/wiki/ESP32"&gt;&lt;span class="caps"&gt;ESP32&lt;/span&gt;&lt;/a&gt;. This
post covers some of the ways I work with MicroPython from Ubuntu, and some of
the recent additions to the Ubuntu archive that ease&amp;nbsp;this.&lt;/p&gt;
&lt;p&gt;Grab a favoured beverage, and settle down with your Pico to &amp;#8220;play along&amp;#8221;&amp;nbsp;…&lt;/p&gt;
&lt;div class="section" id="all-things-are-ready-if-our-packages-be-so"&gt;
&lt;h2&gt;All things are ready, if our packages be&amp;nbsp;so&lt;/h2&gt;
&lt;p&gt;Pretty much all communication with MicroPython is done via serial link. In more
advanced cases this involves hooking up wires to the serial pins on the Pico
itself, but that&amp;#8217;s not the focus of this post. We&amp;#8217;re just going to take the
easy route of talking to a serial port emulated over the Micro-&lt;span class="caps"&gt;USB&lt;/span&gt; connection.
The things you&amp;#8217;ll need to follow&amp;nbsp;along:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A computer running Ubuntu 22.10 (Kinetic). This can be a &lt;span class="caps"&gt;PC&lt;/span&gt;, or a Raspberry
Pi. Basically anything with a &lt;span class="caps"&gt;USB&lt;/span&gt;&amp;nbsp;port.&lt;/li&gt;
&lt;li&gt;A Raspberry Pi Pico. This can be the Pico or Pico W; the examples assume the
latter but will work equally well on the former (I&amp;#8217;ll touch on the W&amp;#8217;s WiFi
facilities in a future post). For that matter, these examples should be
easily adaptable to any other &lt;span class="caps"&gt;RP2040&lt;/span&gt; or &lt;span class="caps"&gt;ESP32&lt;/span&gt;-based&amp;nbsp;boards.&lt;/li&gt;
&lt;li&gt;A micro-&lt;span class="caps"&gt;USB&lt;/span&gt; cable to connect your Ubuntu computer to your&amp;nbsp;Pico.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We start by installing a MicroPython firmware on your Pico. The &lt;a class="reference external" href="https://www.raspberrypi.com/documentation/microcontrollers/micropython.html"&gt;Raspberry Pi
MicroPython&lt;/a&gt; documentation covers this perfectly but a quick overview would&amp;nbsp;be:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://micropython.org/download/"&gt;Download the firmware&lt;/a&gt; (a file with a &amp;#8220;.uf2&amp;#8221; extension) for your specific
board; the Pico and Pico W have separate&amp;nbsp;builds.&lt;/li&gt;
&lt;li&gt;Hold down the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;BOOTSEL&lt;/span&gt;&lt;/tt&gt; button (the only button) on your Pico, while
connecting it to your computer with the micro-&lt;span class="caps"&gt;USB&lt;/span&gt;&amp;nbsp;cable.&lt;/li&gt;
&lt;li&gt;Release the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;BOOTSEL&lt;/span&gt;&lt;/tt&gt; button once it&amp;#8217;s&amp;nbsp;connected.&lt;/li&gt;
&lt;li&gt;After a few seconds you should see a &lt;span class="caps"&gt;USB&lt;/span&gt; storage drive called &amp;#8220;&lt;span class="caps"&gt;RPI&lt;/span&gt;-&lt;span class="caps"&gt;RP2&lt;/span&gt;&amp;#8221;
appear on your computer. Open this device in your file&amp;nbsp;explorer.&lt;/li&gt;
&lt;li&gt;Copy the &amp;#8220;.uf2&amp;#8221; file you downloaded over to the &lt;span class="caps"&gt;RPI&lt;/span&gt;-&lt;span class="caps"&gt;RP2&lt;/span&gt;&amp;nbsp;drive.&lt;/li&gt;
&lt;li&gt;After a few seconds the &amp;#8220;&lt;span class="caps"&gt;RPI&lt;/span&gt;-&lt;span class="caps"&gt;RP2&lt;/span&gt;&amp;#8221; drive should disappear; this indicates the
Pico has flashed the firmware and rebooted into the MicroPython&amp;nbsp;prompt.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that the Pico&amp;#8217;s set, let&amp;#8217;s install some new toys on your Ubuntu machine.
The rshell tool was new in jammy (22.04), but mpremote is new to kinetic&amp;nbsp;(22.10):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pyboard-rshell&lt;span class="w"&gt; &lt;/span&gt;micropython-mpremote
&lt;/pre&gt;
&lt;p&gt;You may be wondering why I&amp;#8217;ve excluded &amp;#8220;&lt;a class="reference external" href="https://thonny.org/"&gt;thonny&lt;/a&gt;&amp;#8221; here. Partly that&amp;#8217;s because
the Raspberry Pi documentation covers it perfectly already. However, it&amp;#8217;s also
because these two command line tools have some interesting facilities unique to
them, and they&amp;#8217;re how I tend to work with&amp;nbsp;MicroPython.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-fault-is-not-in-our-cable-but-in-our-file-modes"&gt;
&lt;h2&gt;The fault … is not in our cable, but in our file&amp;nbsp;modes&lt;/h2&gt;
&lt;p&gt;Both these tools communicate over a serial connection with MicroPython running
on the microcontroller, so the first thing we need to establish is … which
serial device? The vast majority of the time, it will be &lt;tt class="docutils literal"&gt;/dev/ttyACM0&lt;/tt&gt; but
you can double check this by starting a terminal window and&amp;nbsp;running:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dmesg&lt;span class="w"&gt; &lt;/span&gt;-w
&lt;/pre&gt;
&lt;p&gt;This will dump the kernel ring buffer (basically the kernel log) and &amp;#8220;follow&amp;#8221;
it, printing out new messages as they occur. Now plug the Pico back in again,
and you should see something like the following&amp;nbsp;appear:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[24893.432219] usb 1-2.4: new full-speed USB device number 13 using xhci_hcd
[24893.759502] usb 1-2.4: New USB device found, idVendor=2e8a, idProduct=0005, bcdDevice= 1.00
[24893.759506] usb 1-2.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[24893.759508] usb 1-2.4: Product: Board in FS mode
[24893.759509] usb 1-2.4: Manufacturer: MicroPython
[24893.759510] usb 1-2.4: SerialNumber: 1234567890abcdef
[24893.778722] cdc_acm 1-2.4:1.0: ttyACM0: USB ACM device
&lt;/pre&gt;
&lt;p&gt;That last line is the important one, indicating that the new serial device is
called &lt;tt class="docutils literal"&gt;ttyACM0&lt;/tt&gt;, so the device path is simply &lt;tt class="docutils literal"&gt;/dev/ttyACM0&lt;/tt&gt;. Next we
should check that your user has access to this serial&amp;nbsp;device:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;/dev/ttyACM0&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;crw-rw---- 1 root dialout 166, 0 Oct 19 16:01 /dev/ttyACM0
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;groups&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;ubuntu adm dialout cdrom floppy sudo audio dip video plugdev netdev lxd&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Note that the serial device belongs to user &lt;tt class="docutils literal"&gt;root&lt;/tt&gt; and group &lt;tt class="docutils literal"&gt;dialout&lt;/tt&gt;, and
that our user also belongs to the &lt;tt class="docutils literal"&gt;dialout&lt;/tt&gt; group. If you have any issues
talking to MicroPython, this is the first thing to check. On server Ubuntu
images, the default user belongs to the &lt;tt class="docutils literal"&gt;dialout&lt;/tt&gt; group but sadly this isn&amp;#8217;t
the case on Ubuntu desktop images (&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/user-setup/+bug/1923363"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1923363&lt;/a&gt;). If your user does &lt;em&gt;not&lt;/em&gt;
belong to the &lt;tt class="docutils literal"&gt;dialout&lt;/tt&gt; group, run the&amp;nbsp;following:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;adduser&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dialout
&lt;/pre&gt;
&lt;p&gt;Afterwards, log out and log back in again (group membership changes are only
evaluated at login&amp;nbsp;time).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="and-make-a-heaven-of-rshell"&gt;
&lt;h2&gt;… and make a heaven of&amp;nbsp;rshell&lt;/h2&gt;
&lt;p&gt;The first thing to try is just to get to the MicroPython &lt;span class="caps"&gt;REPL&lt;/span&gt; (the interactive
prompt). We&amp;#8217;ll export &lt;tt class="docutils literal"&gt;RSHELL_PORT&lt;/tt&gt; so we don&amp;#8217;t have to continually specify
the port on the command line (if your port is &lt;tt class="docutils literal"&gt;/dev/ttyACM0&lt;/tt&gt;, that&amp;#8217;s the
default anyway but it never hurts to be&amp;nbsp;certain):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;RSHELL_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyACM0&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;rshell&lt;span class="w"&gt; &lt;/span&gt;repl&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Using buffer-size of 32
Connecting to /dev/ttyACM0 (buffer-size 32)...
Trying to connect to REPL  connected
Retrieving sysname ... rp2
Testing if ubinascii.unhexlify exists ... Y
Retrieving root directories ...
Setting time ... Oct 19, 2022 17:42:43
Evaluating board_name ... pyboard
Retrieving time epoch ... Jan 01, 1970
Entering REPL. Use Control-X to exit.
&amp;gt;
MicroPython v1.19.1 on 2022-10-14; Raspberry Pi Pico W with RP2040
Type &amp;quot;help()&amp;quot; for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; import sys
&amp;gt;&amp;gt;&amp;gt; sys.version_info
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(3, 4, 0)&lt;/span&gt;

&lt;/pre&gt;
&lt;p&gt;Several things to note here. Firstly, the message &amp;#8220;Entering &lt;span class="caps"&gt;REPL&lt;/span&gt;. Use Control-X
to exit&amp;#8221;. Now you know how to get back to your command line! Secondly, that the
current version of MicroPython is (mostly) compatible with the Python 3.4
language specification. Actually, it includes some &lt;a class="reference external" href="https://docs.micropython.org/en/latest/genrst/index.html"&gt;features from later
versions&lt;/a&gt; too (like f-strings from version 3.6), but these aren&amp;#8217;t
feature-complete&amp;nbsp;yet:&lt;/p&gt;
&lt;pre class="code pycon literal-block"&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Compatible with Python &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;'Compatible with Python 3.4.0'&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Now, hit Ctrl-X to quit the MicroPython &lt;span class="caps"&gt;REPL&lt;/span&gt;, and let&amp;#8217;s explore the Pico&amp;#8217;s
file-system. &lt;tt class="docutils literal"&gt;rshell&lt;/tt&gt; implements a number of common file operations including
&lt;tt class="docutils literal"&gt;ls&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;cp&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;rm&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;cat&lt;/tt&gt; but the most important detail is that it
considers the Pico&amp;#8217;s file-system to exist under the path &lt;tt class="docutils literal"&gt;/pyboard/&lt;/tt&gt;. If we
list that path currently we&amp;#8217;ll find it&amp;#8217;s&amp;nbsp;empty:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ rshell --quiet ls /pyboard/
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;We used &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--quiet&lt;/span&gt;&lt;/tt&gt; to suppress all that &amp;#8220;connecting&amp;#8221; gubbins that rshell
tends to spit&amp;nbsp;out.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s create a basic script for MicroPython to run on startup, copy it to the
board and run it on boot. The script we&amp;#8217;ll create is the hardware equivalent of
the classic &amp;#8220;Hello, world!&amp;#8221; script: blinking an &lt;span class="caps"&gt;LED&lt;/span&gt;. Fire up your favourite
editor and enter the following&amp;nbsp;script:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;main.py&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;machine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;led&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LED'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;5 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;7 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This script should work on a Pico W, but on a Pico the built-in &lt;span class="caps"&gt;LED&lt;/span&gt; is wired
slightly differently. If you&amp;#8217;ve got a Pico, change &lt;tt class="docutils literal"&gt;'&lt;span class="caps"&gt;LED&lt;/span&gt;'&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;25&lt;/tt&gt; (which
is the &lt;span class="caps"&gt;GPIO&lt;/span&gt; pin the internal &lt;span class="caps"&gt;LED&lt;/span&gt; is connected&amp;nbsp;to).&lt;/p&gt;
&lt;p&gt;Save this script as &lt;tt class="docutils literal"&gt;main.py&lt;/tt&gt; and then copy it to your connected Pico like&amp;nbsp;so:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;rshell&lt;span class="w"&gt; &lt;/span&gt;--quiet&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;main.py&lt;span class="w"&gt; &lt;/span&gt;/pyboard/&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Copying '/home/dave/main.py' to '/pyboard/main.py' ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;rshell&lt;span class="w"&gt; &lt;/span&gt;--quiet&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/pyboard/&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;main.py&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;So far, so good, but nothing seems to be happening yet. That&amp;#8217;s because we need
to reset the Pico to get the script running. There&amp;#8217;s a few ways to do this: the
simplest is to unplug and then re-connect the Pico. That works but is rather
unsatisfying as it also reset the serial connection which doesn&amp;#8217;t help with
debugging. The other method is to start the &lt;span class="caps"&gt;REPL&lt;/span&gt; and reset MicroPython with the
common Ctrl+D&amp;nbsp;shortcut:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;rshell&lt;span class="w"&gt; &lt;/span&gt;--quiet&lt;span class="w"&gt; &lt;/span&gt;repl&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Entering REPL. Use Control-X to exit.
&amp;gt;
MicroPython v1.19.1 on 2022-10-14; Raspberry Pi Pico W with RP2040
Type &amp;quot;help()&amp;quot; for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; ^D
MPY: soft reboot&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;^D&lt;/tt&gt; above indicates where I pressed Ctrl+D. At this point your &lt;span class="caps"&gt;LED&lt;/span&gt;
should be happily blinking away happily but … what happened to our &lt;span class="caps"&gt;REPL&lt;/span&gt;? It&amp;#8217;s
still there, but it&amp;#8217;s sat in our &lt;tt class="docutils literal"&gt;while&lt;/tt&gt; loop. In other words, we won&amp;#8217;t get
our prompt back until we break out of the loop. Press Ctrl+C to do so and you
should get back to the regular MicroPython&amp;nbsp;prompt:&lt;/p&gt;
&lt;pre class="code pycon literal-block"&gt;
&lt;span class="go"&gt;^C
&lt;/span&gt;&lt;span class="gt"&gt;Traceback (most recent call last):
&lt;/span&gt;  File &lt;span class="nb"&gt;&amp;quot;main.py&amp;quot;&lt;/span&gt;, line &lt;span class="m"&gt;7&lt;/span&gt;, in &lt;span class="n"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gr"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="w"&gt;:
&lt;/span&gt;&lt;span class="x"&gt;MicroPython v1.19.1 on 2022-10-14; Raspberry Pi Pico W with RP2040
Type &amp;quot;help()&amp;quot; for more information.
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Of course, having done that our program has also stopped (the &lt;span class="caps"&gt;LED&lt;/span&gt; won&amp;#8217;t be
blinking any more), but usefully the &lt;span class="caps"&gt;REPL&lt;/span&gt; is still running in the context of
our (terminated) &lt;tt class="docutils literal"&gt;main.py&lt;/tt&gt; script. In other words, we can poke around its
globals and even manipulate&amp;nbsp;things:&lt;/p&gt;
&lt;pre class="code pycon literal-block"&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Pin(WL_GPIO0, mode=OUT)
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Generally speaking, this is how I handle the vast majority of my debugging
needs in MicroPython. Either a few carefully inserted &lt;tt class="docutils literal"&gt;print&lt;/tt&gt; statements in
my program (which can then be watched from the &lt;span class="caps"&gt;REPL&lt;/span&gt;), or hitting Ctrl+C at some
appropriate point and poking around the program&amp;#8217;s state are generally&amp;nbsp;sufficient.&lt;/p&gt;
&lt;p&gt;How about debugging a more complex example? The following is a &lt;a class="reference external" href="https://waldorf.waveform.org.uk/examples/morse-1.py"&gt;simple script&lt;/a&gt; that blinks the &lt;span class="caps"&gt;LED&lt;/span&gt; with a morse-code&amp;nbsp;message:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;main.py&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;machine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;morse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;dah&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;intra_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;inter_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dah&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;inter_word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'O'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'---'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inter_word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inter_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intra_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dah&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'invalid morse char!'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;led&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LED'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;morse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SOS'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;morse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PICO'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If we upload this as &lt;tt class="docutils literal"&gt;main.py&lt;/tt&gt; and run it, we find the second call to
&lt;tt class="docutils literal"&gt;morse&lt;/tt&gt; will fail because we haven&amp;#8217;t got enough characters in our &lt;tt class="docutils literal"&gt;codes&lt;/tt&gt;
dictionary:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;rshell&lt;span class="w"&gt; &lt;/span&gt;--quiet&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;main.py&lt;span class="w"&gt; &lt;/span&gt;/pyboard/&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Copying '/home/dave/main.py' to '/pyboard/main.py' ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;rshell&lt;span class="w"&gt; &lt;/span&gt;--quiet&lt;span class="w"&gt; &lt;/span&gt;repl&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Entering REPL. Use Control-X to exit.
&amp;gt;
MicroPython v1.19.1 on 2022-10-14; Raspberry Pi Pico W with RP2040
Type &amp;quot;help()&amp;quot; for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; ^D
MPY: soft reboot
Traceback (most recent call last):
  File &amp;quot;main.py&amp;quot;, line 28, in &amp;lt;module&amp;gt;
  File &amp;quot;main.py&amp;quot;, line 15, in morse
KeyError: P
MicroPython v1.19.1 on 2022-10-14; Raspberry Pi Pico W with RP2040
Type &amp;quot;help()&amp;quot; for more information.
&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;How could we determine this if we didn&amp;#8217;t understand the exception that&amp;#8217;s
thrown? The experienced Python user may be tempted to throw something like
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;print(repr(locals()))&lt;/span&gt;&lt;/tt&gt; in the &lt;tt class="docutils literal"&gt;morse&lt;/tt&gt; function to dump the local variables
after they&amp;#8217;re set up. However, MicroPython has some subtle differences to
CPython and one of them is that &lt;a class="reference external" href="https://docs.micropython.org/en/latest/genrst/core_language.html#local-variables-aren-t-included-in-locals-result"&gt;locals doesn&amp;#8217;t really work&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is one of the reasons a lot of MicroPython scripts use more globals than
might typically be considered &amp;#8220;good practice&amp;#8221; in regular Python. Another is
that MicroPython scripts (necessarily) tend to be a lot simpler, often fitting
in a single module so spilling state into globals isn&amp;#8217;t quite such an egregious
habit&amp;nbsp;there.&lt;/p&gt;
&lt;p&gt;Hence, in this case if I were confused about the &lt;tt class="docutils literal"&gt;KeyError&lt;/tt&gt; I might move most
of the locals in &lt;tt class="docutils literal"&gt;morse&lt;/tt&gt; into the global namespace and dump the state from
within the function. In this case the top of the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/examples/morse-2.py"&gt;updated script&lt;/a&gt; looks something like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;main.py&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;machine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;fatal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;dit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;dah&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;intra_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;inter_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dah&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;inter_word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'O'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'---'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;morse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inter_word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;At this point, I could query &lt;tt class="docutils literal"&gt;codes&lt;/tt&gt; from the &lt;span class="caps"&gt;REPL&lt;/span&gt; after the failure and
probably figure things out. This approach can even be advantageous to your
script anyway as it guarantees that these variables aren&amp;#8217;t being recalculated
on each run of &lt;tt class="docutils literal"&gt;morse&lt;/tt&gt; (programming for a micro-controller sometimes requires
a somewhat different mindset to programming for a &amp;#8220;full-blown&amp;#8221;&amp;nbsp;computer).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="under-baud-s-heavy-burden-do-i-rsync"&gt;
&lt;h2&gt;Under baud&amp;#8217;s heavy burden do I&amp;nbsp;rsync&lt;/h2&gt;
&lt;p&gt;What about investigation of something &amp;#8220;after the fact&amp;#8221;, when the Pico&amp;#8217;s &lt;em&gt;not&lt;/em&gt;
plugged into a serial console? In this case there&amp;#8217;s two methods I tend to turn&amp;nbsp;to:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Take a leaf out of Linux&amp;#8217;s book: log stuff to text&amp;nbsp;files!&lt;/li&gt;
&lt;li&gt;Take a hint from the Pi&amp;#8217;s bootloader (and the script above): blink the &lt;span class="caps"&gt;LED&lt;/span&gt;
to indicate different error&amp;nbsp;conditions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We have to be bit careful with the first point: we don&amp;#8217;t have the practically
infinite logging space that we&amp;#8217;d have on a &lt;span class="caps"&gt;PC&lt;/span&gt;. For that reason I tend to have
my error logging routines start their file from scratch on each run. I also
combine a &lt;tt class="docutils literal"&gt;fatal&lt;/tt&gt; routine (which should log an exception) with an infinite
blinking&amp;nbsp;routine.&lt;/p&gt;
&lt;p&gt;The following is a slightly cut-down version of a &lt;a class="reference external" href="https://waldorf.waveform.org.uk/examples/logging.py"&gt;logging.py module&lt;/a&gt; I often&amp;nbsp;use:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;logging.py&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;machine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;log_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'log.txt'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_msg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;04d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;02d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;02d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;T&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;02d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;02d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;02d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;-8s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;log_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;format_msg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;log_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'debug'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;29 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'info'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;31 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'warning'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;33 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;35 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;36 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;37 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;38 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;39 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;40 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;41 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;42 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;43 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;44 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blink&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;45 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;46 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;47 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uname&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;48 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s1"&gt;'Pico W'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;49 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'LED'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;50 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="s1"&gt;'Pico'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;51 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;52 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;53 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'Cannot blink: unknown board &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;54 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;55 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;led&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;56 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;57 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;58 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;59 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;60 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;61 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;62 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;There is an &lt;a class="reference external" href="https://github.com/micropython/micropython-lib/tree/master/python-stdlib/logging"&gt;official port&lt;/a&gt;
of the CPython logging module for MicroPython but personally I consider
that overkill for most MicroPython logging&amp;nbsp;needs.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;And here&amp;#8217;s our updated morse script (still broken!) with some sensible logging
calls, and a catch-all at the end which will dump the (otherwise unhandled)
exception to the log file and enter an infinite blinking loop to warn us that
something&amp;#8217;s gone horribly&amp;nbsp;wrong:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;main.py&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;machine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;dit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;dah&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;intra_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;inter_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dah&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;inter_word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'S'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'O'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'---'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;morse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'Blinking &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inter_word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inter_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intra_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dah&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'invalid morse char!'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;                &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;29 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;31 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;led&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LED'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;morse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SOS'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;33 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;morse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PICO'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;35 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blink&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We&amp;#8217;re now getting into multiple files that need to be kept up to date between
our computer and the Pico. We can&amp;#8217;t exactly run &lt;tt class="docutils literal"&gt;git&lt;/tt&gt; on the Pico, but
&lt;tt class="docutils literal"&gt;rshell&lt;/tt&gt; still has some tricks up its sleeve to make maintaining this a bit
easier in the form of a (very basic) &lt;tt class="docutils literal"&gt;rsync&lt;/tt&gt; implementation:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;rshell&lt;span class="w"&gt; &lt;/span&gt;--quiet&lt;span class="w"&gt; &lt;/span&gt;rsync&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;/pyboard/&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Checking /pyboard/main.py
Checking /pyboard/logging.py
/home/dave/projects/home/pico/morse/logging.py is newer than /pyboard/logging.py - copying&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This makes development cycles of Pico setups with multiple files &lt;em&gt;much&lt;/em&gt; faster.
Unfortunately there&amp;#8217;s &lt;a class="reference external" href="https://github.com/dhylands/rshell/issues/201"&gt;currently an issue&lt;/a&gt; with this when the host&amp;#8217;s
clock isn&amp;#8217;t set to &lt;span class="caps"&gt;UTC&lt;/span&gt;. Personally I just set my Pi&amp;#8217;s clock to &lt;span class="caps"&gt;UTC&lt;/span&gt; to work
around this, but I should take a proper look when I get a&amp;nbsp;second!&lt;/p&gt;
&lt;p&gt;Now, once we reset the board we get the initial &amp;#8220;&lt;span class="caps"&gt;SOS&lt;/span&gt;&amp;#8221; blink, then a steady
triple-blink on the internal &lt;span class="caps"&gt;LED&lt;/span&gt; when things have failed, and we can query the
log file like&amp;nbsp;so:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;rshell&lt;span class="w"&gt; &lt;/span&gt;--quiet&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/pyboard/log.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;2022-10-19T22:58:24 info     Blinking 'SOS'
2022-10-19T22:58:28 info     Blinking 'PICO'
2022-10-19T22:58:29 error    Traceback (most recent call last):
2022-10-19T22:58:29 error      File &amp;quot;main.py&amp;quot;, line 33, in &amp;lt;module&amp;gt;
2022-10-19T22:58:29 error      File &amp;quot;main.py&amp;quot;, line 19, in morse
2022-10-19T22:58:29 error    KeyError: P&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;We could change the final &lt;tt class="docutils literal"&gt;except&lt;/tt&gt; clause to blink differently for differing
exceptions, e.g. 4 blinks for a socket issue, 3 blinks for other &lt;span class="caps"&gt;IO&lt;/span&gt; errors, 2
blinks for all other&amp;nbsp;exceptions.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s enough on &lt;tt class="docutils literal"&gt;rshell&lt;/tt&gt; for now. The package includes a full man-page,
&lt;a class="reference external" href="https://manpages.ubuntu.com/manpages/kinetic/man1/rshell.1.html"&gt;rshell(1)&lt;/a&gt;,
which I would strongly recommend reading for all the other features it&amp;nbsp;includes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="mount-mount-my-soul"&gt;
&lt;h2&gt;Mount, mount, my&amp;nbsp;soul!&lt;/h2&gt;
&lt;p&gt;This brings us to the new stuff in Ubuntu Kinetic (22.10): the &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt;
utility. Unlike &lt;tt class="docutils literal"&gt;rshell&lt;/tt&gt; which is a third-party tool (though still one of my
favourites for interfacing with MicroPython), &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt; is straight from the
MicroPython developers&amp;nbsp;themselves.&lt;/p&gt;
&lt;p&gt;The first thing to deal with is how we specify the serial port for the
MicroPython board in &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt;. The full syntax is &lt;tt class="docutils literal"&gt;connect &amp;lt;device&amp;gt;&lt;/tt&gt;, for&amp;nbsp;example:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;connect&lt;span class="w"&gt; &lt;/span&gt;/dev/ttyACM0&lt;span class="w"&gt; &lt;/span&gt;repl&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] to exit this shell

&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;The exit sequence under &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt; is &lt;tt class="docutils literal"&gt;Ctrl+[&lt;/tt&gt; rather than &lt;tt class="docutils literal"&gt;Ctrl+X&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;However, this is a bit of a mouthful (fingerful?) to type so &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt; ships
with a whole load of built-in aliases for common serial&amp;nbsp;devices:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;--help&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;mpremote -- MicroPython remote control
See https://docs.micropython.org/en/latest/reference/mpremote.html

List of commands:
  connect     connect to given device
  disconnect  disconnect current device
  edit        edit files on the device
  eval        evaluate and print the string
  exec        execute the string
  fs          execute filesystem commands on the device
  help        print help and exit
  mip         install packages from micropython-lib or third-party sources
  mount       mount local directory on device
  repl        connect to given device
  resume      resume a previous mpremote session (will not auto soft-reset)
  run         run the given local script
  soft-reset  perform a soft-reset of the device
  umount      unmount the local directory
  version     print version and exit

List of shortcuts:
  --help
  --version
  a0          connect to serial port &amp;quot;/dev/ttyACM0&amp;quot;
  a1          connect to serial port &amp;quot;/dev/ttyACM1&amp;quot;
  a2          connect to serial port &amp;quot;/dev/ttyACM2&amp;quot;
  a3          connect to serial port &amp;quot;/dev/ttyACM3&amp;quot;
  bootloader  make the device enter its bootloader
  c0          connect to serial port &amp;quot;COM0&amp;quot;
  c1          connect to serial port &amp;quot;COM1&amp;quot;
  c2          connect to serial port &amp;quot;COM2&amp;quot;
  c3          connect to serial port &amp;quot;COM3&amp;quot;
  cat
  cp
  devs        list available serial ports
  df
  ls
  mkdir
  reset       reset the device after delay
  rm
  rmdir
  setrtc
  touch
  u0          connect to serial port &amp;quot;/dev/ttyUSB0&amp;quot;
  u1          connect to serial port &amp;quot;/dev/ttyUSB1&amp;quot;
  u2          connect to serial port &amp;quot;/dev/ttyUSB2&amp;quot;
  u3          connect to serial port &amp;quot;/dev/ttyUSB3&amp;quot;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Here we can see that &lt;tt class="docutils literal"&gt;a0&lt;/tt&gt; is an alias for &lt;tt class="docutils literal"&gt;connect /dev/ttyACM0&lt;/tt&gt; so we can
use this&amp;nbsp;instead:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;a0&lt;span class="w"&gt; &lt;/span&gt;repl&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] to exit this shell

&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt; utility includes many of the same facilities as &lt;tt class="docutils literal"&gt;rshell&lt;/tt&gt;
like &lt;tt class="docutils literal"&gt;ls&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;cp&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;rm&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;cat&lt;/tt&gt; but with a different idea of the
location of the remote file-system. Unlike &lt;tt class="docutils literal"&gt;rshell&lt;/tt&gt; which pretends the Pico
is mounted under &lt;tt class="docutils literal"&gt;/pyboard/&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt; prefixes MicroPython paths with a&amp;nbsp;colon:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;a0&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;ls :
     327 log.txt
    1387 logging.py
     807 main.py
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;a0&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;*.py&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;cp logging.py :
cp main.py :
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;a0&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;:log.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;2022-10-19T22:58:24 info     Blinking 'SOS'
2022-10-19T22:58:28 info     Blinking 'PICO'
2022-10-19T22:58:29 error    Traceback (most recent call last):
2022-10-19T22:58:29 error      File &amp;quot;main.py&amp;quot;, line 33, in &amp;lt;module&amp;gt;
2022-10-19T22:58:29 error      File &amp;quot;main.py&amp;quot;, line 19, in morse
2022-10-19T22:58:29 error    KeyError: P&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;However, there&amp;#8217;s one particular facility that&amp;#8217;s unique to &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt;: the
&lt;tt class="docutils literal"&gt;mount&lt;/tt&gt; sub-command. This allows you to mount a directory on your computer on
the MicroPython board under the &lt;tt class="docutils literal"&gt;/remote&lt;/tt&gt; directory:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;a0&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Local directory . is mounted at /remote
Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] to exit this shell
&amp;gt;
MicroPython v1.19.1 on 2022-10-14; Raspberry Pi Pico W with RP2040
Type &amp;quot;help()&amp;quot; for more information.
&amp;gt;&amp;gt;&amp;gt; import os
&amp;gt;&amp;gt;&amp;gt; os.listdir('.')
['main.py', 'logging.py']
&amp;gt;&amp;gt;&amp;gt; os.listdir('/')
['remote', 'log.txt', 'logging.py', 'main.py']&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;The &amp;#8220;mount&amp;#8221; only persists as long as &lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt; is&amp;nbsp;running.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;How does this help us? You can edit your MicroPython code on your computer, and
have your MicroPython board run it straight from there without constantly
copying stuff back and forth! When you run &lt;tt class="docutils literal"&gt;mount .&lt;/tt&gt; the MicroPython &lt;span class="caps"&gt;REPL&lt;/span&gt;
that&amp;#8217;s launched is already in the &lt;tt class="docutils literal"&gt;/remote&lt;/tt&gt; path (as you can see in the
snippet above where listing &amp;#8220;.&amp;#8221; shows only &lt;tt class="docutils literal"&gt;main.py&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;logging.py&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;In this case we almost certainly &lt;em&gt;don&amp;#8217;t&lt;/em&gt; want the MicroPython interpreter
running a &lt;tt class="docutils literal"&gt;main.py&lt;/tt&gt; script when it starts; we just want it to drop to the
&lt;span class="caps"&gt;REPL&lt;/span&gt; and let us run our script manually. So the first thing to do is remove
&lt;tt class="docutils literal"&gt;:main.py&lt;/tt&gt; which is the copy of &lt;tt class="docutils literal"&gt;main.py&lt;/tt&gt; on the board. Then we&amp;#8217;ll mount
our directory on the board, and manually run our script by importing &lt;tt class="docutils literal"&gt;main&lt;/tt&gt;.
As expected, it&amp;#8217;ll blink &lt;span class="caps"&gt;SOS&lt;/span&gt;, then fail but afterwards (once we kill the script
with Ctrl+C, then exit the &lt;span class="caps"&gt;REPL&lt;/span&gt; with Ctrl+]) we should have a &amp;#8220;log.txt&amp;#8221; &lt;em&gt;on the
computer&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;a0&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;main.py&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;rm :main.py
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;a0&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;ls :
     327 log.txt
    1394 logging.py
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;logging.py  main.py
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mpremote&lt;span class="w"&gt; &lt;/span&gt;a0&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Local directory . is mounted at /remote
Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] to exit this shell
&amp;gt;
MicroPython v1.19.1 on 2022-10-14; Raspberry Pi Pico W with RP2040
Type &amp;quot;help()&amp;quot; for more information.
&amp;gt;&amp;gt;&amp;gt; import main
2022-10-21T10:10:45 info     Blinking 'SOS'
2022-10-21T10:10:50 info     Blinking 'PICO'
2022-10-21T10:10:51 error    Traceback (most recent call last):
2022-10-21T10:10:51 error      File &amp;quot;main.py&amp;quot;, line 33, in &amp;lt;module&amp;gt;
2022-10-21T10:10:51 error      File &amp;quot;main.py&amp;quot;, line 19, in morse
2022-10-21T10:10:51 error    KeyError: P
^C
Traceback (most recent call last):
  File &amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;, line 1, in &amp;lt;module&amp;gt;
  File &amp;quot;main.py&amp;quot;, line 35, in &amp;lt;module&amp;gt;
  File &amp;quot;logging.py&amp;quot;, line 59, in fatal
KeyboardInterrupt:
&amp;gt;&amp;gt;&amp;gt; ^]
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;logging.py  log.txt  main.py
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;log.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;2022-10-21T10:10:45 info     Blinking 'SOS'
2022-10-21T10:10:50 info     Blinking 'PICO'
2022-10-21T10:10:51 error    Traceback (most recent call last):
2022-10-21T10:10:51 error      File &amp;quot;main.py&amp;quot;, line 33, in &amp;lt;module&amp;gt;
2022-10-21T10:10:51 error      File &amp;quot;main.py&amp;quot;, line 19, in morse
2022-10-21T10:10:51 error    KeyError: P&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;We could now edit our scripts on the computer (in another terminal) and then
immediately retry them on the board without any&amp;nbsp;copying!&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p class="last"&gt;One word of caution here: the remote mount effectively performs file-system
operations over the &lt;span class="caps"&gt;UART&lt;/span&gt;. Mostly this is not significantly different but
there are some cases where performance or behaviour is substantially
changed. For instance, lots of small reads or writes are likely to be
slower than on the local flash storage, and &lt;tt class="docutils literal"&gt;os.statvfs&lt;/tt&gt; currently raises
an exception when called on the remote&amp;nbsp;mount.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="exit-pursued-by-baudelaire"&gt;
&lt;h2&gt;Exit, pursued by&amp;nbsp;Baudelaire&lt;/h2&gt;
&lt;p&gt;That&amp;#8217;s quite enough waffling from me! I blame the editor (&lt;em&gt;who he? &amp;#8212;Ed&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s plenty more detail in the man-pages of both &lt;tt class="docutils literal"&gt;rshell&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;mpremote&lt;/tt&gt;, and an absolute &lt;em&gt;ton&lt;/em&gt; of &lt;a class="reference external" href="https://micropython.org/download/"&gt;MicroPython capable boards&lt;/a&gt; out there
to explore, not to mention the &lt;a class="reference external" href="https://thepihut.com/collections/pico"&gt;myriad&lt;/a&gt; &lt;a class="reference external" href="https://shop.pimoroni.com/collections/pico"&gt;things&lt;/a&gt; you can &lt;a class="reference external" href="https://www.adafruit.com/category/875"&gt;attach&lt;/a&gt; to&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;Have&amp;nbsp;fun!&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="pico"></category><category term="kinetic"></category><category term="micropython"></category></entry><entry><title>Hyping pixels</title><link href="https://waldorf.waveform.org.uk/2022/hyping-pixels.html" rel="alternate"></link><published>2022-10-18T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2022-10-18:/2022/hyping-pixels.html</id><summary type="html">&lt;p class="first last"&gt;Getting the Pimoroni Hyperpixel display working on&amp;nbsp;Ubuntu&lt;/p&gt;
</summary><content type="html">&lt;p&gt;One of the features I&amp;#8217;ve been taking a look at in the Ubuntu Kinetic cycle is
getting as many different display types working out of the box (or as nearly
out of the box as is reasonable) on Ubuntu. The &lt;a class="reference external" href="https://www.raspberrypi.com/products/raspberry-pi-touch-display/"&gt;official Raspberry Pi touch
display&lt;/a&gt; is still a work in progress (it&amp;#8217;s working with the &lt;span class="caps"&gt;FKMS&lt;/span&gt; overlay, but
that&amp;#8217;s not sustainable for the future, and there&amp;#8217;s still issues using it with
&lt;span class="caps"&gt;KMS&lt;/span&gt; under&amp;nbsp;Ubuntu).&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s some interesting &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Electronic_paper"&gt;e-Ink&lt;/a&gt; displays which I&amp;#8217;m also working on, but this
post is about Pimoroni&amp;#8217;s awesome little &lt;a class="reference external" href="https://shop.pimoroni.com/search?q=hyperpixel"&gt;Hyperpixel&lt;/a&gt; range of&amp;nbsp;displays.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="Pinhead from Clive Barker's Hellraiser is holding a Hyperpixel, declaring &amp;quot;We have such rainbows to show you&amp;quot;." src="https://waldorf.waveform.org.uk/images/hyperpixel-pinhead.jpg" /&gt;
&lt;p class="caption"&gt;Pinhead hasn&amp;#8217;t quite understood &amp;#8220;using all the&amp;nbsp;pins&amp;#8221;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="in-the-beginning"&gt;
&lt;h2&gt;In the&amp;nbsp;beginning&lt;/h2&gt;
&lt;p&gt;In which Dave, yet again, buries the lede&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;Some may be aware that the graphics stack on the Raspberry Pi has undergone
some upheaval of late. For those not following along, things are quite rapidly
(by the standards of these things) moving from the original (largely
closed-source) stack to an open&amp;nbsp;one.&lt;/p&gt;
&lt;p&gt;In the old stack (referred to as &amp;#8220;fkms&amp;#8221; which, ever so slightly confusingly,
means &amp;#8220;fake&amp;#8221; &lt;span class="caps"&gt;KMS&lt;/span&gt;) most graphical operations were handled by a closed-source
firmware, running on the &lt;span class="caps"&gt;GPU&lt;/span&gt; which Linux interacted with via the &lt;span class="caps"&gt;VC4&lt;/span&gt; &amp;#8220;mailbox&amp;#8221;.
In the new stack (referred to as &amp;#8220;full&amp;#8221; &lt;span class="caps"&gt;KMS&lt;/span&gt;, but the &amp;#8220;full&amp;#8221; is definitely not
&amp;#8220;fake&amp;#8221; so it&amp;#8217;s just &amp;#8220;kms&amp;#8221; for short), the Linux kernel is in charge of stuff&amp;nbsp;directly.&lt;/p&gt;
&lt;p&gt;Ultimately the idea is that, without all the special cases sloshing around in
closed-source blobs stuff should Just Work™. Naturally, the real world isn&amp;#8217;t
quite so simple but in the case of the Hyperpixel it really is pretty damned
close to this. Or at least, it turned out to be considerably easier than I&amp;nbsp;expected!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="darkness-moving-over-the-water"&gt;
&lt;h2&gt;Darkness moving over the&amp;nbsp;water&lt;/h2&gt;
&lt;p&gt;The nicest thing about the new stack is that the Hyperpixel can be treated as a
regular display, and there are &lt;em&gt;no&lt;/em&gt; extra dependencies. In other words, it can
be made to work from first boot … sort of. We&amp;#8217;ll get into the short-comings a
bit further on, but for now here&amp;#8217;s what you do to get&amp;nbsp;going.&lt;/p&gt;
&lt;p&gt;The Hyperpixel uses &lt;span class="caps"&gt;DPI&lt;/span&gt; (&lt;a class="reference external" href="https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#parallel-display-interface-dpi"&gt;Display Parallel Interface&lt;/a&gt;) to receive its imagery.
Unfortunately this requires a &lt;em&gt;lot&lt;/em&gt; of pins. Nearly &lt;a class="reference external" href="https://pinout.xyz/pinout/dpi#"&gt;all the &lt;span class="caps"&gt;GPIO&lt;/span&gt; pins&lt;/a&gt; in&amp;nbsp;fact.&lt;/p&gt;
&lt;p&gt;The default Ubuntu configuration reserves some of the &lt;span class="caps"&gt;GPIO&lt;/span&gt; pins for
interfaces like I²C, &lt;span class="caps"&gt;SPI&lt;/span&gt;, and serial &lt;span class="caps"&gt;UART&lt;/span&gt;. We need to disable all that so the
display can use them&amp;nbsp;instead:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;We&amp;#8217;ll start with a freshly flashed, unbooted card containing Ubuntu 22.10
(Kinetic). Either the desktop or the server version is&amp;nbsp;fine.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Stick the card in another machine (running any &lt;span class="caps"&gt;OS&lt;/span&gt;) with an &lt;span class="caps"&gt;SD&lt;/span&gt; card reader so
we can edit the&amp;nbsp;configuration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Open &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; on the boot partition (which will be the only partition
that shows up if you&amp;#8217;re doing this on Mac &lt;span class="caps"&gt;OS&lt;/span&gt; X or Windows, or the one
labelled &amp;#8220;system-boot&amp;#8221;&amp;nbsp;otherwise)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Disable the I²C and &lt;span class="caps"&gt;SPI&lt;/span&gt; interfaces. This is simply a matter of deleting the
following lines (or placing a &amp;#8220;#&amp;#8221; in front of them to comment them&amp;nbsp;out):&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;i2c_arm=on&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;spi=on&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you are using the desktop image, skip to step&amp;nbsp;8.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Delete (or comment out) the line enabling the serial console. Again, this
uses pins that the Hyperpixel&amp;nbsp;needs:&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="na"&gt;enable_uart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Open &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; and remove &lt;tt class="docutils literal"&gt;console=serial0,115200&lt;/tt&gt; from the start of
the line (you cannot comment out bits of the kernel command line, so just
delete this bit). This is the only change required in &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt;, so
close it and switch back to &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Add the following line at the end of the&amp;nbsp;file:&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you have a rectangular Hyperpixel display, add the following line at the
end of the&amp;nbsp;file:&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-dpi-hyperpixel4&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;If you have a &lt;a class="reference external" href="https://shop.pimoroni.com/products/hyperpixel-4-square"&gt;square Hyperpixel&lt;/a&gt; display, add the following line&amp;nbsp;instead:&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-dpi-hyperpixel4sq&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you need to rotate the display, add the appropriate parameters; the
&lt;tt class="docutils literal"&gt;rotate&lt;/tt&gt; parameter rotates the display clockwise, while the &lt;tt class="docutils literal"&gt;touchscreen&lt;/tt&gt;
parameters transpose or invert the coordinates reported by the touchscreen
which allows you to configure an equivalent&amp;nbsp;rotation:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;No&amp;nbsp;rotation&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;For non-square Hyperpixel displays this will be a portrait orientation
with the top of the display by the &lt;span class="caps"&gt;USB&lt;/span&gt; and Ethernet ports (if any).
No extra configuration&amp;nbsp;required.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;90°&amp;nbsp;rotation&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first"&gt;For non-square Hyperpixel displays this will be a landscape orientation
with the left of the display by the &lt;span class="caps"&gt;USB&lt;/span&gt; and Ethernet ports (if any).
Add the following&amp;nbsp;lines:&lt;/p&gt;
&lt;pre class="code ini last literal-block"&gt;
&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;rotate=90&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;touchscreen-swapped-x-y,touchscreen-inverted-y&lt;/span&gt;
&lt;/pre&gt;
&lt;/dd&gt;
&lt;dt&gt;180°&amp;nbsp;rotation&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first"&gt;For non-square Hyperpixel displays this will be a portrait orientation
with the bottom of the display by the &lt;span class="caps"&gt;USB&lt;/span&gt; and Ethernet ports (if any).
Add the following&amp;nbsp;lines:&lt;/p&gt;
&lt;pre class="code ini last literal-block"&gt;
&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;rotate=180&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;touchscreen-inverted-x,touchscreen-inverted-y&lt;/span&gt;
&lt;/pre&gt;
&lt;/dd&gt;
&lt;dt&gt;270°&amp;nbsp;rotation&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first"&gt;For non-square Hyperpixel displays this will be a portrait orientation
with the right of the display by the &lt;span class="caps"&gt;USB&lt;/span&gt; and Ethernet ports (if any).
Add the following&amp;nbsp;lines:&lt;/p&gt;
&lt;pre class="code ini last literal-block"&gt;
&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;rotate=270&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;touchscreen-swapped-x-y,touchscreen-inverted-x&lt;/span&gt;
&lt;/pre&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, if you started with an Ubuntu Server image, a rectangular
Hyperpixel, and you want 180° rotation, you should wind up with a
&lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; that looks like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="na"&gt;arm_boost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c1"&gt;# Enable the audio output, I2C and SPI interfaces on the GPIO header. As these&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c1"&gt;# parameters related to the base device-tree they must appear *before* any&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;# other dtoverlay= specification&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;audio=on&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="c1"&gt;#dtparam=i2c_arm=on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="c1"&gt;#dtparam=spi=on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c1"&gt;# Comment out the following line if the edges of the desktop appear outside&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="c1"&gt;# the edges of your display&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="na"&gt;disable_overscan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="c1"&gt;# If you have issues with audio, you may try uncommenting the following line&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="c1"&gt;# which forces the HDMI output into HDMI mode instead of DVI (which doesn&amp;#39;t&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# support audio output)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;#hdmi_drive=2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="c1"&gt;# Enable the serial pins&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="c1"&gt;#enable_uart=1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="c1"&gt;# Autoload overlays for any recognized cameras or displays that are attached&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="c1"&gt;# to the CSI/DSI ports. Please note this is for libcamera support, *not* for&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="c1"&gt;# the legacy camera stack&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-33"&gt;&lt;span class="linenos"&gt;33&lt;/span&gt;&lt;span class="na"&gt;camera_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-34"&gt;&lt;span class="linenos"&gt;34&lt;/span&gt;&lt;span class="na"&gt;display_auto_detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-35"&gt;&lt;span class="linenos"&gt;35&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-36"&gt;&lt;span class="linenos"&gt;36&lt;/span&gt;&lt;span class="c1"&gt;# Config settings specific to arm64&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-37"&gt;&lt;span class="linenos"&gt;37&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-38"&gt;&lt;span class="linenos"&gt;38&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-39"&gt;&lt;span class="linenos"&gt;39&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-40"&gt;&lt;span class="linenos"&gt;40&lt;/span&gt;&lt;span class="k"&gt;[cm4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-41"&gt;&lt;span class="linenos"&gt;41&lt;/span&gt;&lt;span class="c1"&gt;# Enable the USB2 outputs on the IO board (assuming your CM4 is plugged into&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-42"&gt;&lt;span class="linenos"&gt;42&lt;/span&gt;&lt;span class="c1"&gt;# such a board)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-43"&gt;&lt;span class="linenos"&gt;43&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dwc2,dr_mode=host&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-44"&gt;&lt;span class="linenos"&gt;44&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-45"&gt;&lt;span class="linenos"&gt;45&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-46"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;46&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-v3d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-47"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;47&lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vc4-kms-dpi-hyperpixel4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-48"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;48&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;rotate=180&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-49"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;49&lt;/span&gt;&lt;span class="na"&gt;dtparam&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;touchscreen-inverted-x,touchscreen-inverted-y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;And your &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; should look like&amp;nbsp;this:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 rootwait fixrtc quiet splash
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="let-there-be-rainbows"&gt;
&lt;h2&gt;Let there be&amp;nbsp;rainbows!&lt;/h2&gt;
&lt;p&gt;If you&amp;#8217;re using the desktop edition, you&amp;#8217;ll need a monitor plugged in for the
first time setup. Not because the Hyperpixel won&amp;#8217;t work … but because the setup
daemon won&amp;#8217;t fit on the Hyperpixel. Unfortunately this will become a bit of a
theme as we explore&amp;nbsp;further.&lt;/p&gt;
&lt;p&gt;For that matter, you&amp;#8217;ll probably want a &amp;#8220;proper&amp;#8221; keyboard with the desktop
edition too because the on-screen keyboard is … painfully&amp;nbsp;small!&lt;/p&gt;
&lt;p&gt;Boot up your Pi, and you should see the plymouth &amp;#8220;whirly&amp;#8221; loading screen appear
on &lt;em&gt;both&lt;/em&gt; the monitor and the Hyperpixel. When the initial setup window loads
it will only appear on the monitor. You can try dragging it off the right edge
to appear on the Hyperpixel but it won&amp;#8217;t fit (in either portrait or landscape&amp;nbsp;orientations).&lt;/p&gt;
&lt;p&gt;Once through the initial setup, don&amp;#8217;t detach the monitor just yet. You&amp;#8217;ll need
it to deal with the first login greeter which … also doesn&amp;#8217;t fit (despite being
mostly empty space). That said, a major plus point in Kinetic is that the
Online Accounts system finally works (because the &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/gtk+3.0/+bug/1924251"&gt;embedded webkit component&lt;/a&gt;
was fixed). Once through the first login greeter you can now shut down the Pi,
remove the monitor, and start it up&amp;nbsp;again.&lt;/p&gt;
&lt;p&gt;Note that everything in the boot-process still works, just on the Hyperpixel,
from the plymouth &amp;#8220;whirly&amp;#8221; screen, to the login screen (where you can try the
on-screen keyboard … though personally I&amp;#8217;d recommend sticking to a &amp;#8220;real&amp;#8221;
keyboard!), to the desktop&amp;nbsp;itself.&lt;/p&gt;
&lt;div class="grid col-3 docutils container"&gt;
&lt;img alt="A photo showing the plymouth &amp;quot;whirly-thing&amp;quot; loading screen for Ubuntu Desktop for Raspberry Pi, booting on the Hyperpixel" src="https://waldorf.waveform.org.uk/images/hyperpixel-desktop-boot-1.jpg" /&gt;
&lt;img alt="The gdm login screen running on the Hyperpixel, with the user &amp;quot;Dave Jones&amp;quot; highlighted" src="https://waldorf.waveform.org.uk/images/hyperpixel-desktop-boot-2.jpg" /&gt;
&lt;img alt="Dave's fat finger poised over the teeny-tiny on-screen keyboard about to try and enter his login password (before giving up and re-attaching the conspicuously absent USB keyboard)" src="https://waldorf.waveform.org.uk/images/hyperpixel-desktop-boot-3.jpg" /&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;#8217;d recommend firstly starting up the Settings application. Note how nicely
this fits even in tiny resolution with an odd aspect ratio (well done, those
developers!). Go to &amp;#8220;Ubuntu Desktop&amp;#8221;, and switch on &amp;#8220;Auto-hide the dock&amp;#8221; to get
a little bit more precious horizontal&amp;nbsp;space!&lt;/p&gt;
&lt;div class="grid col-3 docutils container"&gt;
&lt;img alt="Opening the settings from the top-right … control thingy" src="https://waldorf.waveform.org.uk/images/hyperpixel-settings-1.jpg" /&gt;
&lt;img alt="Opening the Ubuntu Desktop page of the settings application" src="https://waldorf.waveform.org.uk/images/hyperpixel-settings-2.jpg" /&gt;
&lt;img alt="Selecting the &amp;quot;Auto-hide dock&amp;quot; option in the desktop settings" src="https://waldorf.waveform.org.uk/images/hyperpixel-settings-3.jpg" /&gt;
&lt;/div&gt;
&lt;p&gt;Explore around a little more, however, and you may find that this ability to
reform an application to fit a small space … is far from universal. The file
browser and calculator are&amp;nbsp;good:&lt;/p&gt;
&lt;div class="grid col-2 docutils container"&gt;
&lt;img alt="Image showing the files application conforming itself nicely to the Hyperpixel's minimal resolution and strange portrait-style aspect ratio" src="https://waldorf.waveform.org.uk/images/hyperpixel-good-files.jpg" /&gt;
&lt;img alt="Another screenshot showing how the calculator likewise adapts to the Hyperpixel's odd dimensions" src="https://waldorf.waveform.org.uk/images/hyperpixel-good-calc.jpg" /&gt;
&lt;/div&gt;
&lt;p&gt;Unfortunately the application selector is effectively unreadable, and the help
page falls off the edge of the screen although the help application itself does
manage to maximize correctly. The Mines game, unfortunately, doesn&amp;#8217;t
(and won&amp;#8217;t) reshape itself to the&amp;nbsp;Hyperpixel:&lt;/p&gt;
&lt;div class="grid col-3 docutils container"&gt;
&lt;img alt="Screenshot showing how the application launcher's icons are hilariously small (and largely truncated)" src="https://waldorf.waveform.org.uk/images/hyperpixel-bad-start.jpg" /&gt;
&lt;img alt="A screenshot of the help system which, despite the overall window adapting nicely, shows how the content falls off the right edge" src="https://waldorf.waveform.org.uk/images/hyperpixel-bad-help.jpg" /&gt;
&lt;img alt="Screenshot of the Mines games refusing to fit on the display (to the point that the maximize button just refuses to do anything despite changing icon)" src="https://waldorf.waveform.org.uk/images/hyperpixel-bad-mines.jpg" /&gt;
&lt;/div&gt;
&lt;p&gt;Firefox works tolerably well though (although the Settings page also falls off
the right&amp;nbsp;side):&lt;/p&gt;
&lt;div class="grid col-2 docutils container"&gt;
&lt;img alt="Screenshot of firefox demonstrating it can be well behaved, with all the controls and the content conforming to the Hyperpixel's size" src="https://waldorf.waveform.org.uk/images/hyperpixel-firefox-privacy.jpg" /&gt;
&lt;img alt="Another screenshot of firefox demonstrating that its own settings don't behave quite so well, by falling off the right edge" src="https://waldorf.waveform.org.uk/images/hyperpixel-firefox-settings.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="kivy-kivy-kivy-a-man"&gt;
&lt;h2&gt;Kivy Kivy Kivy A Man&amp;nbsp;…&lt;/h2&gt;
&lt;p&gt;If you&amp;#8217;re using the server edition, you can just plug in and go but you will
need a keyboard as there&amp;#8217;s no on-screen keyboard you can use at the Linux
console. You should find rotation of the screen is honoured … for&amp;nbsp;now!&lt;/p&gt;
&lt;p&gt;First of all we&amp;#8217;ll try one of the more popular Python-based frameworks for
touchscreen interfaces: &lt;a class="reference external" href="https://kivy.org/"&gt;Kivy&lt;/a&gt;. This is already in the Ubuntu archive, and so are
its examples. However, we do need to make a couple of tweaks to get the most
out of&amp;nbsp;it:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Firstly, add the default user to the &lt;tt class="docutils literal"&gt;input&lt;/tt&gt; group. Without this change,
the touchscreen won&amp;#8217;t work for console-based&amp;nbsp;applications:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;adduser&lt;span class="w"&gt; &lt;/span&gt;ubuntu&lt;span class="w"&gt; &lt;/span&gt;input
&lt;/pre&gt;
&lt;p&gt;For this to take effect, you&amp;#8217;ll need to logout and login again (group
membership is only evaluated at login time; you can use &lt;tt class="docutils literal"&gt;sg&lt;/tt&gt; to switch
groups but this gets confusing to follow with all the&amp;nbsp;sub-shells!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Next we install Kivy, Kivy&amp;#8217;s examples, and the multi-touch library
&lt;tt class="docutils literal"&gt;libmtdev1&lt;/tt&gt; (also necessary to take advantage of certain examples &amp;#8212; I
suspect this is a missing dependency / recommendation of the examples&amp;nbsp;package):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;libmtdev1&lt;span class="w"&gt; &lt;/span&gt;python3-kivy&lt;span class="w"&gt; &lt;/span&gt;python-kivy-examples
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Kivy installs its examples under &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/share/kivy-examples&lt;/span&gt;&lt;/tt&gt;. Feel free to
browse this directory and its contents, trying things for yourself. By way
of example, we&amp;#8217;ll start with a look at Kivy&amp;#8217;s multi-touch and basic
graphics&amp;nbsp;capabilities:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr/share/kivy-examples&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;demo/pictures/main.py&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;tutorials/pong/main.py
&lt;/pre&gt;
&lt;div class="grid col-2 docutils container"&gt;
&lt;img alt="A short animation of moving, rotation, and zooming pictures with the multi-touch panel on the Hyperpixel" src="https://waldorf.waveform.org.uk/images/hyperpixel-pictures.webp" /&gt;
&lt;img alt="A short animation of me playing Pong against myself on the Hyperpixel" src="https://waldorf.waveform.org.uk/images/hyperpixel-pong.webp" /&gt;
&lt;/div&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p class="last"&gt;If you enabled rotation, you may note several things at this point:
firstly that the screen rotation is ignored completely. And secondly
that the touchscreen rotation … isn&amp;#8217;t. Which results in some&amp;nbsp;difficulty!&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;What about slightly more advanced&amp;nbsp;rendering?&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;3Drendering/main.py&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;shader/plasma.py
&lt;/pre&gt;
&lt;div class="grid col-2 docutils container"&gt;
&lt;img alt="A short animation showing the classic OpenGL demo monkey head, spinning around on the Hyperpixel's display" src="https://waldorf.waveform.org.uk/images/hyperpixel-monkey.webp" /&gt;
&lt;img alt="An animation of the Hyperpixel displaying a flowing red, yellow, and purple plasma field" src="https://waldorf.waveform.org.uk/images/hyperpixel-plasma.webp" /&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;And how about &lt;span class="caps"&gt;UI&lt;/span&gt;&amp;nbsp;widgets?&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;settings/main.py
&lt;/pre&gt;
&lt;img alt="A short animation of Dave's finger messing around with the Kivy widgets demo (and interface remarkably reminiscent of Android 4) on the Hyperpixel's touchscreen" src="https://waldorf.waveform.org.uk/images/hyperpixel-widgets.webp" /&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;#8217;d recommend trying other demos under &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/share/kivy-examples&lt;/span&gt;&lt;/tt&gt;, but be
aware that not all of them work: some of the demos are rather out of date and
require Python 2.x, others have extra dependencies, or require &lt;span class="caps"&gt;OS&lt;/span&gt; facilities or
hardware that isn&amp;#8217;t necessarily available (e.g. the Android demos). The list
of demos I failed to get (reliably) running&amp;nbsp;includes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;RST_Editor/main.py&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;android/*&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;camera/main.py&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;demo/multistroke/main.py&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;frameworks/twisted/twisted_app.py&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;keyboard/main.py&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;kinect/kinectviewer.py&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;svg/*&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;widgets/actionbar.py&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;widgets/colorpicker.py&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;widgets/unicode_textinput.py&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Furthermore, as noted above, screen rotation appears to be ignored by Kivy, but
touchscreen rotation &lt;em&gt;isn&amp;#8217;t&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-a-qt"&gt;
&lt;h2&gt;What a&amp;nbsp;Qt!&lt;/h2&gt;
&lt;p&gt;Another popular &lt;span class="caps"&gt;UI&lt;/span&gt; framework is &lt;a class="reference external" href="https://www.qt.io/"&gt;Qt&lt;/a&gt;. You may be familiar with this from the
&lt;span class="caps"&gt;KDE&lt;/span&gt; desktop, but Qt is also perfectly capable of running &lt;em&gt;outside&lt;/em&gt; a full
windowing environment. This makes it an ideal framework for embedded
applications as you can develop and test within the desktop, but then run the
&lt;em&gt;same code&lt;/em&gt; at the console outside the windowed environment, without the
&amp;#8220;weight&amp;#8221; of the full&amp;nbsp;desktop.&lt;/p&gt;
&lt;p&gt;Again, you&amp;#8217;ll need a keyboard here as there&amp;#8217;s no on-screen keyboard in the
server edition. And once again, we need a couple of tweaks and some new
packages&amp;nbsp;installed:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;If you&amp;#8217;re following on from the Kivy examples you&amp;#8217;ll have done this already
but just in case, add the default user to the &lt;tt class="docutils literal"&gt;input&lt;/tt&gt; group. Without this
change, the touchscreen won&amp;#8217;t work for console-based&amp;nbsp;applications:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;adduser&lt;span class="w"&gt; &lt;/span&gt;ubuntu&lt;span class="w"&gt; &lt;/span&gt;input
&lt;/pre&gt;
&lt;p&gt;For this to take effect, you&amp;#8217;ll need to logout and login again (group
membership is only evaluated at login&amp;nbsp;time)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Install the Qt5 examples. No need to install anything extra here; the
examples are pre-compiled and the dependencies are all correct (e.g.
&lt;tt class="docutils literal"&gt;libmtdev1&lt;/tt&gt; is&amp;nbsp;included):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;qtbase5-examples
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;To tell Qt that you want to render direct to the screen you need to set an
environment&amp;nbsp;variable:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;QT_QPA_PLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eglfs
&lt;/pre&gt;
&lt;p&gt;The Qt documentation has more information on &lt;a class="reference external" href="https://doc.qt.io/qt-6/embedded-linux.html"&gt;platform plugins for embedded
devices&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Qt&amp;#8217;s examples are stored under &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/lib/$GNU_ARCH/qt5/examples&lt;/span&gt;&lt;/tt&gt;. We&amp;#8217;ll
switch to that directory and try a simple demo that includes controls for
exiting&amp;nbsp;it:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr/lib/aarch64-linux-gnu/qt5/examples&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;./widgets/touch/fingerpaint/fingerpaint
&lt;/pre&gt;
&lt;img alt="A short animation of Dave's finger messing around with the Qt5 fingerpaint demo, showing the Hyperpixel's multi-touch facility" src="https://waldorf.waveform.org.uk/images/hyperpixel-fingerpaint.webp" /&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are &lt;em&gt;many&lt;/em&gt; other examples under the &lt;tt class="docutils literal"&gt;qt5/examples&lt;/tt&gt; directory, but I
would caution against trying them immediately. Firstly, set up &lt;span class="caps"&gt;SSH&lt;/span&gt; to your Pi
from another machine. Most of the Qt examples assume they&amp;#8217;re in a windowing
environment with a convenient &amp;#8220;Close&amp;#8221; button on the owning window. But that&amp;#8217;s
not the case when running from the console, and unlike the Kivy examples you
can&amp;#8217;t simply Escape or Ctrl+C your way out of them. If you find yourself in a
demo you can&amp;#8217;t quit, you can either reboot with the classic Ctrl+Alt+Del
shuffle, or if you have &lt;span class="caps"&gt;SSH&lt;/span&gt; set up you can login from another machine and
&lt;tt class="docutils literal"&gt;killall&lt;/tt&gt; on the demo you&amp;#8217;re&amp;nbsp;running.&lt;/p&gt;
&lt;p&gt;As with Kivy, if you play around with rotation you&amp;#8217;ll notice that screen
rotation is ignored. This can be worked around by using the
&lt;tt class="docutils literal"&gt;QT_QPA_EGLFS_ROTATION&lt;/tt&gt; environment variable to tell Qt to handle the
rotation itself but there are&amp;nbsp;caveats:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;270° rotates incorrectly for some reason&amp;nbsp;(bug?)&lt;/li&gt;
&lt;li&gt;Rotation is &lt;em&gt;anti-clockwise&lt;/em&gt;, the opposite direction to the overlay&amp;#8217;s sense
of&amp;nbsp;rotation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hence, if you wish the touchscreen coordinates to match you&amp;#8217;ll need to apply
opposite&amp;nbsp;measures:&lt;/p&gt;
&lt;div class="grid col-2 docutils container"&gt;
&lt;div class="figure"&gt;
&lt;img alt="A short animation demonstrating a confusing disagreement in the coordinate space between the touchscreen and the display. Dave's finger attempts to draw several shapes but the drawing appears at a different location to that which is touched" src="https://waldorf.waveform.org.uk/images/hyperpixel-fingerpaint-bad-rotate.webp" /&gt;
&lt;p class="caption"&gt;A strange disagreement in coordinate&amp;nbsp;systems!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="figure"&gt;
&lt;img alt="A short animation demonstrating a successful rotation of the touchscreen and the display." src="https://waldorf.waveform.org.uk/images/hyperpixel-fingerpaint-good-rotate.webp" /&gt;
&lt;p class="caption"&gt;But rotation can be achieved with a little&amp;nbsp;work&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s all for now, folks. Have&amp;nbsp;fun!&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="hyperpixel"></category><category term="kinetic"></category></entry><entry><title>The One Where Dave Breaks Stuff</title><link href="https://waldorf.waveform.org.uk/2022/the-one-where-dave-breaks-stuff.html" rel="alternate"></link><published>2022-10-10T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2022-10-10:/2022/the-one-where-dave-breaks-stuff.html</id><summary type="html">&lt;p class="first last"&gt;What&amp;#8217;s happening to RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; (and &lt;span class="caps"&gt;GPIO&lt;/span&gt; handling more generally) in
kinetic, why it&amp;#8217;s happening (now), and how to avoid (some?)&amp;nbsp;breakage&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;Updated with rpi-lgpio&amp;nbsp;instructions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a follow-on from the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/the-pins-they-are-a-changin.html"&gt;prior post&lt;/a&gt; about the &lt;em&gt;ever so slightly&lt;/em&gt; dodgy
state of userspace &lt;span class="caps"&gt;GPIO&lt;/span&gt; on the Raspberry Pi. If you want the full detail, I&amp;#8217;d
strongly recommend going and reading that article first. However, it&amp;#8217;s in my
typically waffley style, so here&amp;#8217;s a quick-ish &lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt; for those that don&amp;#8217;t want
to wade through my excessive&amp;nbsp;verbiage:&lt;/p&gt;
&lt;div class="section" id="previously-on"&gt;
&lt;h2&gt;Previously&amp;nbsp;On…&lt;/h2&gt;
&lt;p&gt;&lt;span class="caps"&gt;GPIO&lt;/span&gt; handling on Pi can be roughly split into &amp;#8220;traditional&amp;#8221; libraries and
&amp;#8220;new&amp;#8221; style&amp;nbsp;libraries.&lt;/p&gt;
&lt;p&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;Traditional&amp;#8221; libraries bang directly on the &lt;span class="caps"&gt;GPIO&lt;/span&gt; registers for most stuff,
by-passing the kernel and hoping not to tread on other &lt;span class="caps"&gt;GPIO&lt;/span&gt; users&amp;#8217; toes. They
also use the (long deprecated) sysfs &lt;span class="caps"&gt;GPIO&lt;/span&gt; controls for some things (usually
edge-detection). &amp;#8220;Traditional&amp;#8221; libraries include the extremely popular
&lt;a class="reference external" href="https://pypi.org/project/RPi.GPIO/"&gt;RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;&lt;/a&gt;, &lt;a class="reference external" href="http://wiringpi.com/"&gt;WiringPi&lt;/a&gt;, &lt;a class="reference external" href="https://abyz.me.uk/rpi/pigpio/"&gt;pigpio&lt;/a&gt;, the legacy &lt;a class="reference external" href="https://pypi.org/project/RPIO/"&gt;&lt;span class="caps"&gt;RPIO&lt;/span&gt;&lt;/a&gt;, and a few&amp;nbsp;others.&lt;/p&gt;
&lt;p&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;New&amp;#8221; style libraries (which include &lt;a class="reference external" href="http://abyz.me.uk/lg/py_lgpio.html"&gt;libgpiod&lt;/a&gt;, and the excellent &lt;a class="reference external" href="http://abyz.me.uk/lg/py_lgpio.html"&gt;lgpio&lt;/a&gt;)
operate the &lt;span class="caps"&gt;GPIO&lt;/span&gt; pins via the kernel&amp;#8217;s gpiochip devices (&lt;tt class="docutils literal"&gt;/dev/gpiochip*&lt;/tt&gt;).
This is a bit more cumbersome as applications must &amp;#8220;reserve&amp;#8221; the pins they want
to use, and it&amp;#8217;s &lt;em&gt;slightly&lt;/em&gt; slower than banging on the hardware registers
directly. However, it guarantees two applications can&amp;#8217;t tread on each others
toes by using the same &lt;span class="caps"&gt;GPIO&lt;/span&gt;; &lt;span class="caps"&gt;GPIO&lt;/span&gt; resources are automatically cleaned up on
process termination (because the kernel knows which process is using which
&lt;span class="caps"&gt;GPIO&lt;/span&gt;), and at least until now it&amp;#8217;s also backwards compatible with the sysfs
&lt;span class="caps"&gt;GPIO&lt;/span&gt;&amp;nbsp;interface.&lt;/p&gt;
&lt;p&gt;Oh, and it&amp;#8217;s not deprecated and scheduled for&amp;nbsp;removal…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-bell-tolls"&gt;
&lt;h2&gt;The Bell&amp;nbsp;Tolls&lt;/h2&gt;
&lt;p&gt;Actually, it&amp;#8217;s been tolling for a couple of years now, but I don&amp;#8217;t think most
of the people that&amp;#8217;ll be directly affected have actually noticed. Take a look
at the section on &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;sysfs-gpio&lt;/span&gt;&lt;/tt&gt; under &lt;a class="reference external" href="https://docs.kernel.org/admin-guide/abi-obsolete.html#abi-sys-class-gpio"&gt;&lt;span class="caps"&gt;ABI&lt;/span&gt; obsolete symbols&lt;/a&gt; in the Linux
kernel documentation and you&amp;#8217;ll find the&amp;nbsp;following:&lt;/p&gt;
&lt;blockquote&gt;
This &lt;span class="caps"&gt;ABI&lt;/span&gt; is deprecated and will be removed after 2020. It is replaced with
the &lt;span class="caps"&gt;GPIO&lt;/span&gt; character device.&lt;/blockquote&gt;
&lt;p&gt;Just to hammer the message home, the top of the &lt;a class="reference external" href="https://docs.kernel.org/admin-guide/gpio/sysfs.html"&gt;&lt;span class="caps"&gt;GPIO&lt;/span&gt; Sysfs Interface for
Userspace&lt;/a&gt; documentation has the following &lt;span class="caps"&gt;SHOUTY&lt;/span&gt; &lt;span class="caps"&gt;WARNING&lt;/span&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;span class="caps"&gt;THIS&lt;/span&gt; &lt;span class="caps"&gt;ABI&lt;/span&gt; &lt;span class="caps"&gt;IS&lt;/span&gt; &lt;span class="caps"&gt;DEPRECATED&lt;/span&gt;, &lt;span class="caps"&gt;THE&lt;/span&gt; &lt;span class="caps"&gt;ABI&lt;/span&gt; &lt;span class="caps"&gt;DOCUMENTATION&lt;/span&gt; &lt;span class="caps"&gt;HAS&lt;/span&gt; &lt;span class="caps"&gt;BEEN&lt;/span&gt; &lt;span class="caps"&gt;MOVED&lt;/span&gt; &lt;span class="caps"&gt;TO&lt;/span&gt;
Documentation/&lt;span class="caps"&gt;ABI&lt;/span&gt;/obsolete/sysfs-gpio &lt;span class="caps"&gt;AND&lt;/span&gt; &lt;span class="caps"&gt;NEW&lt;/span&gt; &lt;span class="caps"&gt;USERSPACE&lt;/span&gt; &lt;span class="caps"&gt;CONSUMERS&lt;/span&gt; &lt;span class="caps"&gt;ARE&lt;/span&gt;
&lt;span class="caps"&gt;SUPPOSED&lt;/span&gt; &lt;span class="caps"&gt;TO&lt;/span&gt; &lt;span class="caps"&gt;USE&lt;/span&gt; &lt;span class="caps"&gt;THE&lt;/span&gt; &lt;span class="caps"&gt;CHARACTER&lt;/span&gt; &lt;span class="caps"&gt;DEVICE&lt;/span&gt; &lt;span class="caps"&gt;ABI&lt;/span&gt;. &lt;span class="caps"&gt;THIS&lt;/span&gt; &lt;span class="caps"&gt;OLD&lt;/span&gt; &lt;span class="caps"&gt;SYSFS&lt;/span&gt; &lt;span class="caps"&gt;ABI&lt;/span&gt; &lt;span class="caps"&gt;WILL&lt;/span&gt; &lt;span class="caps"&gt;NOT&lt;/span&gt; &lt;span class="caps"&gt;BE&lt;/span&gt;
&lt;span class="caps"&gt;DEVELOPED&lt;/span&gt; (&lt;span class="caps"&gt;NO&lt;/span&gt; &lt;span class="caps"&gt;NEW&lt;/span&gt; &lt;span class="caps"&gt;FEATURES&lt;/span&gt;), &lt;span class="caps"&gt;IT&lt;/span&gt; &lt;span class="caps"&gt;WILL&lt;/span&gt; &lt;span class="caps"&gt;JUST&lt;/span&gt; &lt;span class="caps"&gt;BE&lt;/span&gt; &lt;span class="caps"&gt;MAINTAINED&lt;/span&gt;.&lt;/blockquote&gt;
&lt;p&gt;Though as noted above, it won&amp;#8217;t be maintained and will be removed … erm … at
some point after two years ago. As you&amp;#8217;ve no doubt guessed it &lt;em&gt;hasn&amp;#8217;t&lt;/em&gt; been
removed yet (because things like &lt;a class="reference external" href="https://pypi.org/project/RPi.GPIO/"&gt;RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;&lt;/a&gt; still operate happily), but it is
nonetheless &amp;#8220;living on borrowed&amp;nbsp;time&amp;#8221;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="playing-the-piper"&gt;
&lt;h2&gt;Playing the&amp;nbsp;Piper&lt;/h2&gt;
&lt;p&gt;As I mentioned in &lt;a class="reference external" href="https://launchpad.net/bugs/1918583"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1918583&lt;/a&gt;, I didn&amp;#8217;t think it was a good idea to go
actively breaking stuff before an &lt;span class="caps"&gt;LTS&lt;/span&gt; release (Jammy, 22.04). However,
now we&amp;#8217;re coming up to the first interim release (Kinetic, 22.10) and I&amp;#8217;m keen
to see just how much pain we&amp;#8217;re in for in the&amp;nbsp;future.&lt;/p&gt;
&lt;img alt="Zapp Brannigan (from Futurama), in his typically over-confident manner, tells Kif to &amp;quot;fetch the kernel configuration!&amp;quot;" src="https://waldorf.waveform.org.uk/images/kif-fetch-the-kconfig.jpg" /&gt;
&lt;p&gt;To that end, the kernel team are going to disable the &lt;span class="caps"&gt;GPIO&lt;/span&gt; sysfs interface for
the forthcoming 5.19 kernel. To be absolutely&amp;nbsp;clear:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;This &lt;em&gt;will not&lt;/em&gt; affect prior releases; all currently working &lt;span class="caps"&gt;GPIO&lt;/span&gt; libraries
will continue to work in Jammy (22.04) and all releases before&amp;nbsp;that&lt;/li&gt;
&lt;li&gt;This &lt;em&gt;will not&lt;/em&gt; affect Ubuntu Core; it only uses kernels from &lt;span class="caps"&gt;LTS&lt;/span&gt; releases so
again, no change&amp;nbsp;(yet)&lt;/li&gt;
&lt;li&gt;This &lt;em&gt;will&lt;/em&gt; break the traditional libraries (&lt;a class="reference external" href="https://pypi.org/project/RPi.GPIO/"&gt;RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;&lt;/a&gt;, &lt;a class="reference external" href="http://wiringpi.com/"&gt;WiringPi&lt;/a&gt;, et al)
under Kinetic&amp;nbsp;(22.10)&lt;/li&gt;
&lt;li&gt;This &lt;em&gt;should not&lt;/em&gt; break &lt;a class="reference external" href="https://gpiozero.readthedocs.io/"&gt;gpiozero&lt;/a&gt; because that already uses one of the
new-style libraries (specifically &lt;a class="reference external" href="http://abyz.me.uk/lg/py_lgpio.html"&gt;lgpio&lt;/a&gt;) by default on Ubuntu (although it
falls back to RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; if it can&amp;#8217;t find lgpio, or if it&amp;#8217;s been told
explicitly to use another&amp;nbsp;backend)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="something-must-be-done"&gt;
&lt;h2&gt;Something Must Be&amp;nbsp;Done&lt;/h2&gt;
&lt;p&gt;What if it&amp;#8217;s all a disaster, and so many things break that Something Must Be
Done? There&amp;#8217;s obvious and slightly less obvious &amp;#8220;somethings&amp;#8221; that we can turn
to in that&amp;nbsp;case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;The most obvious is &amp;#8220;undo it&amp;#8221;. This is just a configuration option in the
kernel, and as it&amp;#8217;s still there we could just flip it back on for 23.04 and
carry on our merry way. However, that&amp;#8217;s likely not a long term&amp;nbsp;option…&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Slightly less obvious: make compatibility shims. For example, if most
breakage turns out to be in applications relying on RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;, make a library
with the RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; interface which uses lgpio or libgpiod&amp;nbsp;underneath.&lt;/p&gt;
&lt;p&gt;The issue here is we need to know which libraries to shim (RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;?
WiringPi? pigpio? All of the above?), and that the shim will &lt;em&gt;never&lt;/em&gt; be 100%
perfect (if two processes rely on accessing the same &lt;span class="caps"&gt;GPIO&lt;/span&gt;, their assumptions
will still be broken, and they will not work with the&amp;nbsp;shim).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Even less obvious: fix the applications. If only a minority of applications
break (or all that break use different pin libraries) it may be better to
simply migrate those applications to a new-style library. &lt;span class="caps"&gt;GPIO&lt;/span&gt; handling is
usually a minority of most applications that use it, so the patches shouldn&amp;#8217;t
be &lt;em&gt;too&lt;/em&gt; long / hard to&amp;nbsp;generate.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Obviously it&amp;#8217;s premature to jump into some of these options, and the end result
may well be a combination of some of&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;Still, I am anticipating that &lt;a class="reference external" href="https://pypi.org/project/RPi.GPIO/"&gt;RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;&lt;/a&gt; is likely to be one of the major
sources of breakage. To that end, I&amp;#8217;ve already worked on a simplistic shim
which transforms the &lt;a class="reference external" href="http://abyz.me.uk/lg/py_lgpio.html"&gt;lgpio&lt;/a&gt; library into a compatible interface. It&amp;#8217;s called
&lt;a class="reference external" href="https://rpi-lgpio.readthedocs.io/"&gt;rpi-lgpio&lt;/a&gt; and will be landing in Kinetic as the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python3-rpi-lgpio&lt;/span&gt;&lt;/tt&gt;
package:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;python3-rpi-lgpio&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following package was automatically installed and is no longer required:
  rpi.gpio-common
Use 'sudo apt autoremove' to remove it.
The following additional packages will be installed:
  liblgpio1 python3-lgpio
The following packages will be REMOVED:
  python3-rpi.gpio
The following NEW packages will be installed:
  liblgpio1 python3-lgpio python3-rpi-lgpio
0 upgraded, 3 newly installed, 1 to remove and 0 not upgraded.
Need to get 108 kB of archives.
After this operation, 365 kB of additional disk space will be used.
Do you want to continue? [Y/n]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Installing this package &lt;em&gt;will&lt;/em&gt; remove the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python3-rpi.gpio&lt;/span&gt;&lt;/tt&gt; package (because
both define an &lt;tt class="docutils literal"&gt;RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;&lt;/tt&gt; module and so cannot co-exist) but should permit
&lt;em&gt;most&lt;/em&gt; existing applications that rely on RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; to continue operating
normally. The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python3-rpi-lgpio&lt;/span&gt;&lt;/tt&gt; package also &amp;#8220;provides&amp;#8221; (in apt parlance)
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python3-rpi.gpio&lt;/span&gt;&lt;/tt&gt; so it can satisfy any package that &amp;#8220;depends&amp;#8221; on
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python3-rpi.gpio&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="this-is-something"&gt;
&lt;h2&gt;This Is&amp;nbsp;Something&lt;/h2&gt;
&lt;img alt="Lrrr (ruler of Planet Omicron Persei 8) demands &amp;quot;Give us GPIO, or we will lay waste to your cities&amp;quot;!" src="https://waldorf.waveform.org.uk/images/give-us-gpio.jpg" /&gt;
&lt;p&gt;In the meantime, what can &lt;em&gt;you&lt;/em&gt; do? If you&amp;#8217;re relying on &lt;span class="caps"&gt;GPIO&lt;/span&gt; connected devices
on your Pi, I would caution you &lt;em&gt;not&lt;/em&gt; to upgrade production systems to Kinetic
(22.10) when it&amp;#8217;s released without testing them first. The simplest way to test
a Raspberry Pi based setup is simply to clone the &lt;span class="caps"&gt;SD&lt;/span&gt; card and try upgrading
the&amp;nbsp;backup:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Shut down your&amp;nbsp;Pi&lt;/li&gt;
&lt;li&gt;Duplicate the &lt;span class="caps"&gt;SD&lt;/span&gt; card to another one of equal / greater size:&lt;ul&gt;
&lt;li&gt;Assuming you have a single &lt;span class="caps"&gt;SD&lt;/span&gt; card reader at your disposal, with the
device name &lt;tt class="docutils literal"&gt;/dev/mmcblk0&lt;/tt&gt;, place the &amp;#8220;production&amp;#8221; card in the&amp;nbsp;reader&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;sudo umount /dev/mmcblk0p1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;sudo umount /dev/mmcblk0p2&lt;/tt&gt; to
unmount any auto-mounted&amp;nbsp;partitions&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;sudo dd &lt;span class="pre"&gt;if=/dev/mmcblk0&lt;/span&gt; of=backup.img bs=16M status=progress&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Remove the production&amp;nbsp;card&lt;/li&gt;
&lt;li&gt;Insert the blank&amp;nbsp;card&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;sudo dd if=backup.img &lt;span class="pre"&gt;of=/dev/mmcblk0&lt;/span&gt; bs=16M status=progress&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;sudo sync&lt;/tt&gt; just to make sure and remove the cloned&amp;nbsp;card&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Plug the &lt;em&gt;duplicated&lt;/em&gt; &lt;span class="caps"&gt;SD&lt;/span&gt; card back into your Pi and boot it&amp;nbsp;up&lt;/li&gt;
&lt;li&gt;Run through the upgrade to Kinetic: &lt;tt class="docutils literal"&gt;sudo &lt;span class="pre"&gt;do-release-upgrade&lt;/span&gt; &lt;span class="pre"&gt;--devel&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;See what&amp;nbsp;breaks!&lt;/li&gt;
&lt;li&gt;If nothing breaks,&amp;nbsp;great!&lt;/li&gt;
&lt;li&gt;If something breaks, file bugs!&lt;ul&gt;
&lt;li&gt;File against the application itself if it&amp;#8217;s from an Ubuntu&amp;nbsp;package&lt;/li&gt;
&lt;li&gt;File against the &lt;span class="caps"&gt;GPIO&lt;/span&gt; library (e.g. &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/rpi.gpio/+filebug"&gt;file a bug against RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; on
Ubuntu&lt;/a&gt;) it&amp;#8217;s using if the application isn&amp;#8217;t from an Ubuntu package, and
you happen to know which &lt;span class="caps"&gt;GPIO&lt;/span&gt; library is being&amp;nbsp;used&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+filebug"&gt;File against Ubuntu&lt;/a&gt; generally if the application isn&amp;#8217;t from an Ubuntu
package, and you&amp;#8217;ve no idea which &lt;span class="caps"&gt;GPIO&lt;/span&gt; library is being&amp;nbsp;used&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In any of the cases above, tag the bug &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+bugs?field.tag=raspi-image"&gt;raspi-image&lt;/a&gt; and it should get my&amp;nbsp;attention&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the meantime, I&amp;#8217;m keen to hear thoughts and/or suggestions about the
forthcoming &lt;span class="caps"&gt;GPIO&lt;/span&gt; &lt;span class="strike"&gt;apocalypse&lt;/span&gt;&amp;nbsp;changes.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="gpio"></category><category term="gpiozero"></category><category term="kinetic"></category></entry><entry><title>Playing with Blocks - It’s All Connected</title><link href="https://waldorf.waveform.org.uk/2022/playing-with-blocks-its-all-connected.html" rel="alternate"></link><published>2022-09-08T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2022-09-08:/2022/playing-with-blocks-its-all-connected.html</id><summary type="html">&lt;p class="first last"&gt;Making a Pi image with encrypted volume-managed root&amp;nbsp;storage&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;last&lt;/a&gt; &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;two&lt;/a&gt; articles we looked at &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)"&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;&lt;/a&gt; and
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup"&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/a&gt; for some enhancement of the basic storage of the standard Ubuntu Pi
images. This time around we&amp;#8217;ll combine both of those previous articles to make
an image in the same way that the Ubuntu Desktop for &lt;span class="caps"&gt;PC&lt;/span&gt; images install when
they&amp;#8217;re given free reign to overwrite the entire contents of the&amp;nbsp;drive.&lt;/p&gt;
&lt;p&gt;Specifically, this means we aim to wind up&amp;nbsp;with:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A &lt;span class="caps"&gt;FAT&lt;/span&gt; boot partition (no difference there), 512 &lt;span class="caps"&gt;MB&lt;/span&gt; in&amp;nbsp;size&lt;/li&gt;
&lt;li&gt;A second partition taking up the rest of the card containing&lt;ul&gt;
&lt;li&gt;An encrypted &lt;span class="caps"&gt;LUKS&lt;/span&gt; container, containing&lt;ul&gt;
&lt;li&gt;An &lt;span class="caps"&gt;LVM&lt;/span&gt; physical volume (&lt;span class="caps"&gt;PV&lt;/span&gt;) belonging to&lt;ul&gt;
&lt;li&gt;An &lt;span class="caps"&gt;LVM&lt;/span&gt; volume group (&lt;span class="caps"&gt;VG&lt;/span&gt;) called &amp;#8220;pivg&amp;#8221; containing&lt;ul&gt;
&lt;li&gt;An &lt;span class="caps"&gt;LVM&lt;/span&gt; logical volume (&lt;span class="caps"&gt;LV&lt;/span&gt;) called &amp;#8220;root&amp;#8221; taking up some reasonable
portion of the disk space (8 &lt;span class="caps"&gt;GB&lt;/span&gt; in this&amp;nbsp;example)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/luks-lvm-partitions.svg" type="image/svg+xml"&gt;A diagram illustrating the &lt;span class="caps"&gt;SD&lt;/span&gt; card layout showing: a large bar
representing the whole &lt;span class="caps"&gt;SD&lt;/span&gt; card /dev/mmcblk0 (N &lt;span class="caps"&gt;GB&lt;/span&gt;). Beneath
that, two bars: a small one on the left for the system-boot
partition /dev/mmcblk0p1 (512 &lt;span class="caps"&gt;MB&lt;/span&gt;), and a large one on the right
for the remaining partition /dev/mmcblk0p2 (N &lt;span class="caps"&gt;GB&lt;/span&gt; - 512 &lt;span class="caps"&gt;MB&lt;/span&gt;).
Beneath the large right-hand bar, a bar representing the &lt;span class="caps"&gt;LUKS&lt;/span&gt;
encrypted container (again, N &lt;span class="caps"&gt;GB&lt;/span&gt; - 512 &lt;span class="caps"&gt;MB&lt;/span&gt;). Beneath that, two
more bars: a tiny one on the left representing the &lt;span class="caps"&gt;LUKS&lt;/span&gt; header
(16 &lt;span class="caps"&gt;MB&lt;/span&gt;), and a much larger one on the right representing the
&lt;span class="caps"&gt;LVM&lt;/span&gt; &lt;span class="caps"&gt;PV&lt;/span&gt; /dev/mapper/rootfs within the &lt;span class="caps"&gt;LUKS&lt;/span&gt; container (N &lt;span class="caps"&gt;GB&lt;/span&gt; -
512 &lt;span class="caps"&gt;MB&lt;/span&gt; - 16 &lt;span class="caps"&gt;MB&lt;/span&gt;). Lower down, a dotted outline of the same size
representing the &lt;span class="caps"&gt;LVM&lt;/span&gt; &lt;span class="caps"&gt;VG&lt;/span&gt; /dev/pivg. Finally, beneath that, a
smaller bar representing the &lt;span class="caps"&gt;LVM&lt;/span&gt; &lt;span class="caps"&gt;LV&lt;/span&gt; /dev/mapper/pivg-root (8 &lt;span class="caps"&gt;GB&lt;/span&gt;).&lt;/object&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;One thing to note is that I&amp;#8217;ve only tested this with the Ubuntu 22.04
(jammy) server for pi image. Notably, jammy has &lt;tt class="docutils literal"&gt;cryptsetup&lt;/tt&gt; within the
default initramfs on the image. I would not necessarily be confident that
this works on earlier&amp;nbsp;releases.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="three-rowdy-layers"&gt;
&lt;h2&gt;Three (?) Rowdy&amp;nbsp;Layers&lt;/h2&gt;
&lt;p&gt;Rather than go through all the individual commands, which would result in this
post looking like a foreword followed by the dump of a shell script, I&amp;#8217;ll go
through the high level procedure because the detail is almost exactly the same
as the original articles but for a few minor differences and some deliberate&amp;nbsp;omissions.&lt;/p&gt;
&lt;p&gt;Firstly, we&amp;#8217;ll deal with the content of the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;first article&lt;/a&gt; and set up &lt;span class="caps"&gt;LVM&lt;/span&gt;:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Follow the first article instructions until it&amp;#8217;s time to create the &lt;span class="caps"&gt;PV&lt;/span&gt; (with
&lt;tt class="docutils literal"&gt;pvcreate&lt;/tt&gt;):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Plug in the &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card&lt;/li&gt;
&lt;li&gt;Unmount any auto-mounted&amp;nbsp;partitions&lt;/li&gt;
&lt;li&gt;Repartition with &lt;tt class="docutils literal"&gt;gdisk&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;At this point we need to create the &lt;span class="caps"&gt;PV&lt;/span&gt; with &lt;span class="caps"&gt;16MB&lt;/span&gt; &lt;em&gt;less&lt;/em&gt; space than is
available in the second partition, to leave a little room for the &lt;span class="caps"&gt;LUKS&lt;/span&gt;
header around the &lt;span class="caps"&gt;PV&lt;/span&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;From the &lt;tt class="docutils literal"&gt;gdisk&lt;/tt&gt; session, you should have a printout of the new partition
table; if you don&amp;#8217;t, run &lt;tt class="docutils literal"&gt;sudo gdisk &lt;span class="pre"&gt;-l&lt;/span&gt; /dev/mmcblk0&lt;/tt&gt; (assuming
&lt;tt class="docutils literal"&gt;/dev/mmcblk0&lt;/tt&gt; is your &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;For the second partition, calculate the number of sectors it occupies
(which is simply end sector minus start sector) and then subtract 32768
(each sector is 512 bytes, so 32768 is &lt;span class="caps"&gt;16MB&lt;/span&gt;). For example, on a &lt;span class="caps"&gt;32GB&lt;/span&gt; &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;gdisk&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;GPT fdisk (gdisk) version 1.0.8

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/mmcblk0: 62333952 sectors, 29.7 GiB
Model: STORAGE DEVICE
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): A9BDFD97-F2A5-4419-9A83-B0160820FDBB
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 62333918
Partitions will be aligned on 2048-sector boundaries
Total free space is 4061 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1048576   511.0 MiB   0700  system-boot
   2         1050624        62333918   29.2 GiB    8E00  lvm
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nv"&gt;PV_SECTORS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;62333918&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1050624&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32768&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PV_SECTORS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;61250526&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Proceed to creating the logical volume, but explicitly specify the number
of sectors to use when creating the &lt;span class="caps"&gt;PV&lt;/span&gt; with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--setphysicalvolumesize&lt;/span&gt;&lt;/tt&gt;
and the number of sectors with the suffix&amp;nbsp;&amp;#8220;s&amp;#8221;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pvcreate&lt;span class="w"&gt; &lt;/span&gt;--setphysicalvolumesize&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PV_SECTORS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;s&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Physical volume &amp;quot;/dev/mmcblk0p2&amp;quot; successfully created.
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vgcreate&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Volume group &amp;quot;pivg&amp;quot; successfully created
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;lvcreate&lt;span class="w"&gt; &lt;/span&gt;--size&lt;span class="w"&gt; &lt;/span&gt;8G&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Logical volume &amp;quot;root&amp;quot; created.&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Finish the first article&amp;#8217;s&amp;nbsp;instructions:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Unpacking the&amp;nbsp;image&lt;/li&gt;
&lt;li&gt;Creating a loop device for the&amp;nbsp;image&lt;/li&gt;
&lt;li&gt;Transferring the content of the image partitions to the&amp;nbsp;card&lt;/li&gt;
&lt;li&gt;Updating the boot command line and fstab to point to the &amp;#8220;new&amp;#8221; root&amp;nbsp;device&lt;/li&gt;
&lt;li&gt;Unmount the root and boot partitions, and disable the &lt;span class="caps"&gt;VG&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Don&amp;#8217;t&lt;/em&gt; eject the &lt;span class="caps"&gt;SD&lt;/span&gt; card, we still need to do some&amp;nbsp;surgery!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="luks-dujour"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&amp;nbsp;Dujour&lt;/h2&gt;
&lt;p&gt;Now it&amp;#8217;s time to move on to the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;second article&lt;/a&gt; and set up the encrypted &lt;span class="caps"&gt;LUKS&lt;/span&gt;
container around the physical volume (&lt;span class="caps"&gt;PV&lt;/span&gt;):&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Skip everything up to the encryption&amp;nbsp;step:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;No need to download another&amp;nbsp;image&lt;/li&gt;
&lt;li&gt;No need to boot the image&amp;nbsp;yet&lt;/li&gt;
&lt;li&gt;No need to unmount stuff (we already did&amp;nbsp;that)&lt;/li&gt;
&lt;li&gt;No need to shrink partitions (the root partition hasn&amp;#8217;t been auto-expanded
because we haven&amp;#8217;t booted anything, and we&amp;#8217;ve pre-shrunk the &lt;span class="caps"&gt;PV&lt;/span&gt; containing
the&amp;nbsp;root)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Now it&amp;#8217;s time to encrypt the second partition containing the &lt;span class="caps"&gt;PV&lt;/span&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;First the encryption; this is exactly as in the second&amp;nbsp;article:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;reencrypt&lt;span class="w"&gt; &lt;/span&gt;--encrypt&lt;span class="w"&gt; &lt;/span&gt;--reduce-device-size&lt;span class="w"&gt; &lt;/span&gt;16M&lt;span class="w"&gt; &lt;/span&gt;--cipher&lt;span class="w"&gt; &lt;/span&gt;xchacha12,aes-adiantum-plain64&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
WARNING!
========
This will overwrite data on LUKS2-temp-a823bd67-a632-41cb-955e-8776b8b3fb5b.new irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for LUKS2-temp-a823bd67-a632-41cb-955e-8776b8b3fb5b.new:
Verify passphrase:
Finished, time 36:53.687, 29915 MiB written, speed  13.5 MiB/s&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Next we open the encrypted&amp;nbsp;container:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;open&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt; &lt;/span&gt;rootfs&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Enter passphrase for /dev/mmcblk0p2:&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Then we re-scan for physical volumes (PVs) and re-activate the pivg volume
group (&lt;span class="caps"&gt;VG&lt;/span&gt;) we created earlier; this isn&amp;#8217;t in the original article but is
necessary as we&amp;#8217;ve altered the mapper&amp;nbsp;configuration:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pvscan&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;  PV /dev/mapper/rootfs   VG pivg            lvm2 [29.20 GiB / 21.20 GiB free]
  Total: 1 [29.20 GiB] / in use: 1 [29.20 GiB] / in no VG: 0 [0   ]
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vgchange&lt;span class="w"&gt; &lt;/span&gt;-ay&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;  1 logical volume(s) in volume group &amp;quot;pivg&amp;quot; now active&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;We skip over all the resizing bits because we&amp;#8217;ve already established the
size of the root volume, and just mount the root file-system (we don&amp;#8217;t
need to mount the boot file-system). This is, again, slightly different to
the original article because the root file-system is a mapper device,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pivg-root&lt;/span&gt;&lt;/tt&gt; as in the first&amp;nbsp;article:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/mapper/pivg-root&lt;span class="w"&gt; &lt;/span&gt;/mnt/root
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Then a bit (but only a bit) of configuration&amp;nbsp;manipulation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Add our entry to &lt;tt class="docutils literal"&gt;/etc/crypttab&lt;/tt&gt;. Please heed the advice in the original
article regarding the source device here; this is the source device &lt;em&gt;as
seen by the Pi&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&amp;lt;target&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;device&amp;gt;&lt;span class="w"&gt;         &lt;/span&gt;&amp;lt;key&lt;span class="w"&gt; &lt;/span&gt;file&amp;gt;&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;options&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rootfs  /dev/mmcblk0p2  none  luks,discard&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&amp;lt;target&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;device&amp;gt;&lt;span class="w"&gt;         &lt;/span&gt;&amp;lt;key&lt;span class="w"&gt; &lt;/span&gt;file&amp;gt;&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;options&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;rootfs  /dev/mmcblk0p2  none  luks,discard&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Skip the bits about editing &lt;tt class="docutils literal"&gt;fstab&lt;/tt&gt; and the boot command line; we&amp;#8217;ve
already done everything we need to for those. Just unmount root
file-system, and close the encrypted&amp;nbsp;container:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;close&lt;span class="w"&gt; &lt;/span&gt;rootfs
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Now just follow the second article to the&amp;nbsp;end:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Eject the card and boot it on your&amp;nbsp;Pi&lt;/li&gt;
&lt;li&gt;There&amp;#8217;s a &lt;em&gt;long&lt;/em&gt; delay while the initial root mount&amp;nbsp;fails&lt;/li&gt;
&lt;li&gt;In the emergency prompt that appears, open the encrypted device and exit
the&amp;nbsp;prompt&lt;/li&gt;
&lt;li&gt;Once booted, update the&amp;nbsp;initramfs&lt;/li&gt;
&lt;li&gt;Reboot to make sure everything&amp;nbsp;works!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="future-cases"&gt;
&lt;h2&gt;Future&amp;nbsp;Cases&lt;/h2&gt;
&lt;p&gt;Congratulations! At this point you should have a setup eerily similar to the
one I use on my main development Pi, albeit that one boots off a large &lt;span class="caps"&gt;SSD&lt;/span&gt;
instead of an &lt;span class="caps"&gt;SD&lt;/span&gt; card. Future things worth exploring with this&amp;nbsp;setup:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;How to get plymouth prompting nicely for the password; plymouth&amp;#8217;s always been
on the Pi server images but for some reason it doesn&amp;#8217;t work properly (unlike
the Pi desktop images where it&amp;#8217;s happy). I had a cursory look into this a
couple of cycles ago, but didn&amp;#8217;t get anywhere at that&amp;nbsp;time.&lt;/li&gt;
&lt;li&gt;How about ditching password encryption and using a key on removable storage.
For instance, if booting off &lt;span class="caps"&gt;SD&lt;/span&gt; card, how about using a &lt;span class="caps"&gt;USB&lt;/span&gt; key with the
encryption key. Or for my favoured setup where I boot off a &lt;span class="caps"&gt;USB3&lt;/span&gt; attached &lt;span class="caps"&gt;SSD&lt;/span&gt;
drive, how about using an &lt;span class="caps"&gt;SD&lt;/span&gt; card with the&amp;nbsp;key?&lt;/li&gt;
&lt;li&gt;With this configuration I usually set up &lt;span class="caps"&gt;LXD&lt;/span&gt; with an &lt;span class="caps"&gt;LVM&lt;/span&gt; thin-pool for its
default storage. This usually requires taking a crow-bar to &lt;span class="caps"&gt;LXD&lt;/span&gt; and forcing
it to use the thin-pool (with &lt;a class="reference external" href="https://linuxcontainers.org/lxd/docs/master/reference/storage_lvm/#storage-lvm-pool-config"&gt;lvm.vg.force_reuse&lt;/a&gt;; &lt;span class="caps"&gt;LXD&lt;/span&gt; doesn&amp;#8217;t like working
with a non-empty &lt;span class="caps"&gt;VG&lt;/span&gt; for reasons I&amp;#8217;ve never full understood, but at least it&amp;#8217;s
possible). Let me know in the comments if it would be useful to have another
article covering&amp;nbsp;this!&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="ubuntu"></category><category term="storage"></category><category term="lvm"></category><category term="luks"></category></entry><entry><title>Making Jammy less Dodgy</title><link href="https://waldorf.waveform.org.uk/2022/making-jammy-less-dodgy.html" rel="alternate"></link><published>2022-07-22T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2022-07-22:/2022/making-jammy-less-dodgy.html</id><summary type="html">&lt;p class="first last"&gt;Fixing some of the post-release &amp;#8220;fun&amp;#8221; with Jammy (22.04 &lt;span class="caps"&gt;LTS&lt;/span&gt;)&lt;/p&gt;
</summary><content type="html">&lt;img alt="One of Jammy's more &amp;quot;radioactive&amp;quot; wallpapers; a series of waves fading from a bright purple to a positively eye-searing orange. I rather like it!" src="https://waldorf.waveform.org.uk/images/jammy-radioactive-wallpaper.png" /&gt;
&lt;p&gt;It&amp;#8217;s now several months after jammy&amp;#8217;s release, and thus long past time since I
should&amp;#8217;ve posted something about it. I&amp;#8217;ve been holding back on this because,
despite being an &lt;span class="caps"&gt;LTS&lt;/span&gt; (in fact, the first Ubuntu &lt;span class="caps"&gt;LTS&lt;/span&gt; desktop for the Pi), I
can&amp;#8217;t say I&amp;#8217;m happy with&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Yet.&lt;/p&gt;
&lt;p&gt;Still, that&amp;#8217;s why we don&amp;#8217;t enable upgrades for &lt;span class="caps"&gt;LTS&lt;/span&gt; users until the first point
release (&lt;a class="reference external" href="https://discourse.ubuntu.com/t/jammy-jellyfish-release-schedule/23906"&gt;planned for August 4th&lt;/a&gt; at the time of writing). So let&amp;#8217;s go
through the painful bits and see what early adopters can rectify before I get
my nose back to the grindstone and try and fix all the bits I&amp;#8217;ve messed&amp;nbsp;up!&lt;/p&gt;
&lt;p&gt;In some ways, this may be considered a spiritual successor to my &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/6-months-with-the-pi-desktop.html"&gt;earlier
post&lt;/a&gt; (and &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/ubuntu-desktops-on-the-pi.html"&gt;even earlier post&lt;/a&gt;) on the Pi desktops. Or to put it another way,
consider this the current &amp;#8220;Dave recommends you run your Ubuntu Pi this way&amp;#8221;
post (until I supersede it&amp;nbsp;again)!&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;I&amp;#8217;m going to take the unusual step of stating quite explicitly that the
following (like everything on this site) is my &lt;em&gt;personal&lt;/em&gt; opinion. This is
&lt;em&gt;my&lt;/em&gt; recommendation for running your Ubuntu Pi, not anyone else&amp;#8217;s
(including&amp;nbsp;Canonical).&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;So, with disclaimers thoroughly disclaimed, let us begin&amp;nbsp;…&lt;/p&gt;
&lt;div class="section" id="snap"&gt;
&lt;h2&gt;Snap!&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s the &lt;a class="reference external" href="https://www.omgubuntu.co.uk/2022/04/how-to-install-firefox-deb-apt-ubuntu-22-04#disqus_thread"&gt;serial complaint&lt;/a&gt;: the default browser (Firefox) is now a snap. I&amp;#8217;m
not going to get into the fiery debate over snaps here (more beer is required
for that!). Nor am I going to address the start-up speed issues (they&amp;#8217;ve been
&lt;a class="reference external" href="https://ubuntu.com/blog/how-are-we-improving-firefox-snap-performance-part-2"&gt;furiously hacked&lt;/a&gt; upon and &lt;a class="reference external" href="https://ubuntu.com/blog/how-are-we-improving-firefox-snap-performance-part-3"&gt;thoroughly covered&lt;/a&gt; elsewhere; in fact I was a
small cog in the team that did some of that work). Besides, I&amp;#8217;m more concerned
with the performance of the browser &lt;em&gt;after&lt;/em&gt; it&amp;#8217;s started (a browser isn&amp;#8217;t
something I fire up more than once a day), and in that regard I&amp;#8217;ve no
particular complaints about the performance of the snapped version over the deb&amp;nbsp;version.&lt;/p&gt;
&lt;p&gt;And yet, complaints I have&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;There are currently issues with the Firefox snap which, personally, I consider
deal-breakers. In particular &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/chromium-browser/+bug/1741074"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;:&amp;nbsp;#1741074&lt;/a&gt; &amp;#8220;chrome-gnome-shell extension
fails to detect native host connector&amp;#8221; (aka the &amp;#8220;native messaging issue&amp;#8221;). The
bug&amp;#8217;s title may sound relatively innocuous but in practice this translates to
&lt;a class="reference external" href="https://github.com/keepassxreboot/keepassxc-browser/issues/1426"&gt;&amp;#8220;I can&amp;#8217;t use KeepassXC&amp;#8221;&lt;/a&gt; (my password database of choice) with my&amp;nbsp;browser.&lt;/p&gt;
&lt;p&gt;I would fallback to KeepassXC&amp;#8217;s awesome auto-type feature, but that &lt;a class="reference external" href="https://github.com/keepassxreboot/keepassxc/issues/2281#issuecomment-922332932"&gt;doesn&amp;#8217;t
work under Wayland&lt;/a&gt; (to be clear: this isn&amp;#8217;t exactly a defect, more a design
choice; the linked thread has more details). Hence, at least with the version
of jammy as released, my only option to use my (now pretty extensive, and
certainly indispensable) password database is to copy and paste usernames and
passwords via the clipboard … which just about &lt;em&gt;any&lt;/em&gt; process can view.
Obviously, this is a fairly major step backwards in security and not one I&amp;#8217;m
willing to put up&amp;nbsp;with.&lt;/p&gt;
&lt;p&gt;I should stress this is another issue that is top of the list and being
furiously attacked with many sharp objects. Once it&amp;#8217;s fixed, I&amp;#8217;ll have no
specific qualms about returning to the snap (after all, Mozilla seem to prefer
it as a means of distribution). But what to do in the&amp;nbsp;meantime?&lt;/p&gt;
&lt;p&gt;Switch to the flatpak? No good: the aforementioned bug applies to any confined
(snap or otherwise) browser. Besides … the Firefox flatpak is only &lt;a class="reference external" href="https://beta.flathub.org/apps/details/org.mozilla.firefox"&gt;available
for amd64&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Switch to Chromium? It&amp;#8217;s snapped too. That said, I&amp;#8217;m not sure I would even if I
could: &lt;a class="reference external" href="https://github.com/gorhill/uBlock"&gt;uBlock Origin&lt;/a&gt; (an extension I consider damned near mandatory for
anyone wanting to browse the modern web on anything less than a beefy Ryzen
with &lt;span class="caps"&gt;32GB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt;) is &lt;a class="reference external" href="https://github.com/uBlockOrigin/uBlock-issues/issues/338#issuecomment-468339241"&gt;better in Firefox&lt;/a&gt; than in&amp;nbsp;Chromium.&lt;/p&gt;
&lt;p&gt;What about full-fat Chrome? Not an option; Google only distribute Chrome
packages for amd64&amp;nbsp;too.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Rant: frankly, this is one of my biggest misgivings about snap, flatpak,
and all these distribution methods that purport to put &amp;#8220;distribution in the
hands of the developers&amp;#8221;: they all suck at providing for anything other
than &lt;a class="reference external" href="https://github.com/canonical-web-and-design/snapcraft.io/issues/3969"&gt;&lt;span class="caps"&gt;PC&lt;/span&gt; architectures&lt;/a&gt; leaving stuff like the Pi (or &lt;span class="caps"&gt;RISC&lt;/span&gt;-V, or any
new architecture) high and&amp;nbsp;dry.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;So, until this is fixed, let&amp;#8217;s just use the&amp;nbsp;deb:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;add-apt-repository&lt;span class="w"&gt; &lt;/span&gt;ppa:mozillateam/ppa&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... lots of output, press Enter ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;/etc/apt/preferences.d/firefox&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Package: firefox*
Pin: release=LP-PPA-mozillateam
Pin-Priority: 501

Package: firefox*
Pin: release o=Ubuntu
Pin-Priority: -1
EOF
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;purge&lt;span class="w"&gt; &lt;/span&gt;firefox&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;snap&lt;span class="w"&gt; &lt;/span&gt;remove&lt;span class="w"&gt; &lt;/span&gt;firefox&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;firefox
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="crackle"&gt;
&lt;h2&gt;Crackle!&lt;/h2&gt;
&lt;p&gt;Another &lt;a class="reference external" href="https://lists.ubuntu.com/archives/ubuntu-devel/2022-June/042116.html"&gt;cereal complaint&lt;/a&gt; (I make no excuses for the puns here): the
introduction of &lt;a class="reference external" href="https://www.freedesktop.org/software/systemd/man/systemd-oomd.service.html"&gt;systemd-oomd&lt;/a&gt;! In fact, I was rather glad of this one but I
also think it&amp;#8217;s less of an issue on the Pi than it is the &lt;span class="caps"&gt;PC&lt;/span&gt;, for reasons I&amp;#8217;ll
go into&amp;nbsp;…&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;This section is partly redundant because, while writing it, one of my
colleagues (Nick, who despite being fresh-faced and new, jumped headlong
into the fray) pushed a new configuration that substantially solves things.
See &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1972159"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1972159&lt;/a&gt; for the full&amp;nbsp;story.&lt;/p&gt;
&lt;p class="last"&gt;However, there&amp;#8217;s still some useful stuff in here for users on smaller
memory systems (including non-Pis!) so I&amp;#8217;ve left it&amp;nbsp;in.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Firstly, a little context. For those not aware, systemd-oomd is a new facility
for killing memory hogging processes (now activated by default on our desktop
images). Previously, this was solely the domain of the dreaded &lt;a class="reference external" href="https://docs.kernel.org/admin-guide/mm/concepts.html#oom-killer"&gt;&lt;span class="caps"&gt;OOM&lt;/span&gt; killer&lt;/a&gt; in
the Linux kernel. While this certainly did the job of killing memory hogs, it
was typically only capable of doing so &lt;em&gt;after&lt;/em&gt; the system had descended into
&amp;#8220;swap hell&amp;#8221;. In other words, only once something ridiculously fat had pushed
everything else on the system to page itself out to disk, did the kernel &lt;span class="caps"&gt;OOM&lt;/span&gt;
killer wake up and stalk the process table looking for potential&amp;nbsp;victims.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="A frame from Futurama's &amp;quot;Hell is Other Robots&amp;quot; depicting the Beastie Boys scratching a hard drive. A not so subtle allusion to &amp;quot;swap hell&amp;quot;." src="https://waldorf.waveform.org.uk/images/swap-hell.jpg" /&gt;
&lt;p class="caption"&gt;An artist&amp;#8217;s impression of &amp;#8220;swap&amp;nbsp;hell&amp;#8221;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This typically meant that, while things &lt;em&gt;did&lt;/em&gt; get remedied eventually, it took
several minutes of the machine &amp;#8220;thrashing&amp;#8221; for that to happen. In the
noughties, I might listen for the audible cue of an &lt;span class="caps"&gt;HDD&lt;/span&gt; thrashing its heads to
have a clue this was going on, but this is now and SSDs (or &lt;span class="caps"&gt;SD&lt;/span&gt; cards) don&amp;#8217;t
provide quite the same&amp;nbsp;feedback!&lt;/p&gt;
&lt;p&gt;As a rough rule of thumb, people will assume something has crashed after
roughly 5 seconds of the &lt;span class="caps"&gt;UI&lt;/span&gt; appearing to freeze. If that sounds short, consider
how many seconds it would take before you assumed a crash if &lt;em&gt;even your mouse
pointer&lt;/em&gt; is frozen (or jumpy to the point that someone might reasonably assume
something was &amp;#8220;wrong&amp;#8221;). In practice this typically meant that, long before the
kernel &lt;span class="caps"&gt;OOM&lt;/span&gt; killer got involved, the user&amp;#8217;s itchy power-button finger got
involved&amp;nbsp;first.&lt;/p&gt;
&lt;p&gt;To be fair, systemd-oomd does fix several issues with the kernel&amp;#8217;s &lt;span class="caps"&gt;OOM&lt;/span&gt; killer.
The most notable being that a modern application may have many &amp;#8220;processes&amp;#8221; and
(being process-oriented) the kernel&amp;#8217;s &lt;span class="caps"&gt;OOM&lt;/span&gt; killer would only kill the heaviest
individual process (regardless of whether it was a mere component in a larger
system). That potentially left the rest of the application in a unknown /
unstable state. By contrast, systemd-oomd will kill the whole cgroup
consistently (cgroup in this context can be read as &amp;#8220;application&amp;#8221; … sort&amp;nbsp;of).&lt;/p&gt;
&lt;p&gt;It also acts before the system ever gets near &amp;#8220;swap hell&amp;#8221;, basing its actions
on the system reaching some (fairly arbitrary) thresholds like 90% of both
&lt;span class="caps"&gt;RAM&lt;/span&gt; and swap being utilized (to activate), and &lt;a class="reference external" href="https://www.kernel.org/doc/html/latest/accounting/psi.html"&gt;pressure stall information&lt;/a&gt;
(to determine what to act&amp;nbsp;upon).&lt;/p&gt;
&lt;p&gt;Unfortunately there are some major&amp;nbsp;issues:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Most browsers (presumably in acknowledgement of how much fat is dripping off
the silk of the web these days) already have mechanisms to &amp;#8220;unload&amp;#8221; their
heavier processes (usually individual tabs) when they detect the system is
&amp;#8220;under load&amp;#8221;. However, being fine-tuned to the individual application, these
usually kick in later than systemd-oomd will (or to put it another way, by
the time they &lt;em&gt;would&lt;/em&gt; kick in, systemd-oomd has already killed the whole&amp;nbsp;browser).&lt;/li&gt;
&lt;li&gt;systemd-oomd has no user interaction or even notification. One second you&amp;#8217;re
working in your browser and the next: &lt;em&gt;blip&lt;/em&gt;. It&amp;#8217;s gone. No warning, no
chance to intervene, and not so much as an apology after the&amp;nbsp;fact!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the upside, it all happens very quickly: there&amp;#8217;s no wading through the
treacle of swap-thrashing. But it feels … arbitrary, unpredictable, and frankly&amp;nbsp;&amp;#8220;flaky&amp;#8221;.&lt;/p&gt;
&lt;p&gt;There is a simple solution: get more &lt;span class="caps"&gt;RAM&lt;/span&gt;! On my machines with &lt;span class="caps"&gt;8GB&lt;/span&gt; (or more) of
&lt;span class="caps"&gt;RAM&lt;/span&gt;, I simply haven&amp;#8217;t encountered the issue. It&amp;#8217;s only on machines with &lt;span class="caps"&gt;4GB&lt;/span&gt; or
less that this seems to be a serious issue (at least, that&amp;#8217;s my personal
experience &amp;#8212; I&amp;#8217;m sure others with different workloads may find differently).
Still, that includes the Pi 400 I use as a desktop, and also my tiddly laptop
(which has &lt;span class="caps"&gt;4GB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt; and a Celeron processor &amp;#8212; I like my machines small and&amp;nbsp;cheap!).&lt;/p&gt;
&lt;p&gt;And this is where things (surprisingly!) turned out better on the Pi than on
the &lt;span class="caps"&gt;PC&lt;/span&gt;:&lt;/p&gt;
&lt;p&gt;On my little &lt;span class="caps"&gt;4GB&lt;/span&gt; laptop (which doesn&amp;#8217;t really get much use, only when I&amp;#8217;m away
from the house), I noticed Firefox frequently getting killed especially when
working in Google Docs. Curiously, I &lt;em&gt;didn&amp;#8217;t&lt;/em&gt; notice the same on the Pi 400
(one of my two main workhorses that gets used a lot more than the&amp;nbsp;laptop).&lt;/p&gt;
&lt;p&gt;Then I recalled that we&amp;#8217;d activated &lt;a class="reference external" href="https://www.kernel.org/doc/html/v4.19/vm/zswap.html"&gt;zswap&lt;/a&gt; (see &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/6-months-with-the-pi-desktop.html"&gt;earlier post&lt;/a&gt;) by default
on jammy for the Pi desktop images. My &lt;span class="caps"&gt;PC&lt;/span&gt; laptop had the same &lt;span class="caps"&gt;RAM&lt;/span&gt; size (&lt;span class="caps"&gt;4GB&lt;/span&gt;),
the same swap size (&lt;span class="caps"&gt;1GB&lt;/span&gt;), but while zswap was active on the Pi, it wasn&amp;#8217;t on
the laptop. That&amp;#8217;s not to say the Pi was perfect: there were two occasions when
Firefox got killed, but that was in stark contrast to the laptop on which it
happened half a dozen times in a single&amp;nbsp;day.&lt;/p&gt;
&lt;p&gt;Obviously the first thing I did was activate zswap on my laptop and, after
a reboot suddenly things were &lt;em&gt;much&lt;/em&gt; better. I did still manage to get Firefox
killed during a subsequent test run, but after then expanding the swap to &lt;span class="caps"&gt;2GB&lt;/span&gt;
on both machines, I managed a full day of work on the laptop without a single
systemd-oomd&amp;nbsp;activation.&lt;/p&gt;
&lt;p&gt;So (unusually for this site), here&amp;#8217;s a recommendation for any jammy desktop
users on small PCs out there (&lt;strong&gt;Pi users, you can ignore this bit&lt;/strong&gt;):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/GRUB_CMDLINE_LINUX_DEFAULT/ s/splash/splash zswap.enabled=1 zswap.compressor=zstd zswap.zpool=z3fold/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt; &lt;/span&gt;/etc/default/grub&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;zstd&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/initramfs-tools/modules&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;z3fold&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/initramfs-tools/modules&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;update-initramfs&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;update-grub
&lt;/pre&gt;
&lt;p&gt;The above will activate zswap by adding &lt;tt class="docutils literal"&gt;zswap.enabled=1
zswap.compressor=zstd zswap.zpool=z3fold&lt;/tt&gt; to the kernel&amp;#8217;s command line in the
&lt;span class="caps"&gt;GRUB&lt;/span&gt; configuration, and adding the necessary modules to the&amp;nbsp;initramfs.&lt;/p&gt;
&lt;p&gt;Next, I&amp;#8217;d also recommend (to both small &lt;span class="caps"&gt;PC&lt;/span&gt; users &lt;em&gt;and&lt;/em&gt; Pi users), to bump the
default swap-file up to &lt;span class="caps"&gt;2GB&lt;/span&gt;. After doing this, I haven&amp;#8217;t suffered a single
systemd-oomd kill on my Pi 400 (again, for my particular workload &amp;#8212; bear in
mind I spend most of my time in lightweight terminal things, so adjust
accordingly). The following should be run on a freshly booted system as the
first thing we&amp;#8217;re going to do is &lt;em&gt;disable&lt;/em&gt; the existing swap before expanding&amp;nbsp;it:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;swapoff&lt;span class="w"&gt; &lt;/span&gt;/swapfile&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;fallocate&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;2G&lt;span class="w"&gt; &lt;/span&gt;/swapfile&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mkswap&lt;span class="w"&gt; &lt;/span&gt;/swapfile&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/swapfile&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;swapon&lt;span class="w"&gt; &lt;/span&gt;/swapfile
&lt;/pre&gt;
&lt;p&gt;What have we actually accomplished with all&amp;nbsp;this?&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s take the example of my &lt;span class="caps"&gt;4GB&lt;/span&gt; laptop. The defaults for zswap&amp;#8217;s configuration
will reserve 20% of all &lt;span class="caps"&gt;RAM&lt;/span&gt; for compressed pages, which is roughly &lt;span class="caps"&gt;800MB&lt;/span&gt;. Those
compressed pages will (with the z3fold allocator) store &lt;em&gt;up to&lt;/em&gt; 3 uncompressed
pages each. So that &lt;span class="caps"&gt;800MB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt; can act as up to 2.&lt;span class="caps"&gt;4GB&lt;/span&gt; of (suspiciously fast)
swap. And beyond that we also have &lt;span class="caps"&gt;2GB&lt;/span&gt; of (typical, slow) disk-based&amp;nbsp;swap.&lt;/p&gt;
&lt;p&gt;Visually what we&amp;#8217;ve done is&amp;nbsp;this:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/zswap-layout.svg" type="image/svg+xml"&gt;Three bars illustrating the original state of the memory layout (&lt;span class="caps"&gt;4GB&lt;/span&gt;
&lt;span class="caps"&gt;RAM&lt;/span&gt;, &lt;span class="caps"&gt;1GB&lt;/span&gt; swap), the new physical layout (3.&lt;span class="caps"&gt;2GB&lt;/span&gt; &lt;span class="caps"&gt;RAM&lt;/span&gt;, &lt;span class="caps"&gt;800MB&lt;/span&gt; zswap, &lt;span class="caps"&gt;2GB&lt;/span&gt;
disk swap), and the new &amp;#8220;effective&amp;#8221; layout (3.&lt;span class="caps"&gt;2GB&lt;/span&gt; &lt;span class="caps"&gt;RAM&lt;/span&gt;, 2.&lt;span class="caps"&gt;4GB&lt;/span&gt; zswap,
&lt;span class="caps"&gt;2GB&lt;/span&gt; disk swap)&lt;/object&gt;
&lt;p&gt;Don&amp;#8217;t be fooled: we&amp;#8217;ve &lt;em&gt;reduced&lt;/em&gt; the amount of available &lt;span class="caps"&gt;RAM&lt;/span&gt; from &lt;span class="caps"&gt;4GB&lt;/span&gt; to 3.&lt;span class="caps"&gt;2GB&lt;/span&gt;.
This isn&amp;#8217;t good, but it&amp;#8217;s the trade-off you make with compressed &lt;span class="caps"&gt;RAM&lt;/span&gt; systems of
any sort. We&amp;#8217;ve also sacrificed another gig of disk space to swap. However,
we&amp;#8217;ve increased the available swap from &lt;span class="caps"&gt;1GB&lt;/span&gt; (by default) to about 4.&lt;span class="caps"&gt;4GB&lt;/span&gt;, of
which more than half is very fast compressed-memory swap and all for a (fairly)
minimal memory&amp;nbsp;cost.&lt;/p&gt;
&lt;p&gt;Furthermore, we&amp;#8217;ve also moved from a configuration where there&amp;#8217;s &lt;span class="caps"&gt;4GB&lt;/span&gt; of very
fast pages, and &lt;span class="caps"&gt;1GB&lt;/span&gt; of really slow pages to one where there&amp;#8217;s 3.&lt;span class="caps"&gt;2GB&lt;/span&gt; of very
fast pages, 2.&lt;span class="caps"&gt;4GB&lt;/span&gt; of &lt;em&gt;fairly&lt;/em&gt; quick pages, and &lt;span class="caps"&gt;2GB&lt;/span&gt; of really slow pages. In
other words, we&amp;#8217;ve now got a &amp;#8220;smoother&amp;#8221; progression of memory the kernel can
stick pages into. This helps a system under load go a bit slower while
remaining reasonably responsive, instead of suddenly hitting a brick wall of&amp;nbsp;swap.&lt;/p&gt;
&lt;div class="section" id="soggy-cornflakes"&gt;
&lt;h3&gt;Soggy&amp;nbsp;Cornflakes&lt;/h3&gt;
&lt;p&gt;One mea-culpa I should offer here: I managed to forget something rather
important on the Ubuntu Pi desktop images for&amp;nbsp;jammy.&lt;/p&gt;
&lt;p&gt;While zswap is activated, and the swap-file is created (now on boot by the
&lt;tt class="docutils literal"&gt;mkswap.service&lt;/tt&gt; unit so we no longer ship a gigabyte of &amp;#8220;nothing&amp;#8221; in the
image itself) … I managed to forget to add the &amp;#8220;z3fold&amp;#8221; and &amp;#8220;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Zstd"&gt;zstd&lt;/a&gt;&amp;#8221; modules
to the initramfs modules list. As a result, zswap is still working on the Pi
desktop images, but it&amp;#8217;s currently falling back to the default &amp;#8220;zbud&amp;#8221; (2-page)
allocator, and &amp;#8220;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Oberhumer"&gt;lzo&lt;/a&gt;&amp;#8221;&amp;nbsp;compression.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/ubuntu-settings/+bug/1977764"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1977764&lt;/a&gt; is tracking this and I&amp;#8217;ll get it fixed as soon as I reasonably
can (unfortunately, while it sounds trivial it&amp;#8217;s actually a messy one &amp;#8212; read
the bug if you want the full&amp;nbsp;story).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="pop"&gt;
&lt;h2&gt;Pop!&lt;/h2&gt;
&lt;p&gt;That about covers things for the desktop side. What about the server image?
Overall I&amp;#8217;m actually pretty happy with the server side of things, but one thing
has been irking me whenever I&amp;#8217;ve played with the lovely little &lt;a class="reference external" href="https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/"&gt;Pi Zero 2W&lt;/a&gt;.
There&amp;#8217;s precious little memory available at runtime, particularly on the arm64
images (which it seems the &lt;em&gt;vast&lt;/em&gt; majority of our users&amp;nbsp;prefer).&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s been some effort recently to identify things installed by default which
shouldn&amp;#8217;t be, with the goal of reducing the &amp;#8220;base&amp;#8221; memory footprint of our
images and I thought I&amp;#8217;d have a look at what&amp;#8217;s running by default on the Pi
server images to see if there&amp;#8217;s anything we could cull&amp;nbsp;there.&lt;/p&gt;
&lt;p&gt;First, let&amp;#8217;s see how much free memory we have on a fresh&amp;nbsp;boot:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;               total        used        free      shared  buff/cache   available
Mem:           414Mi       152Mi        33Mi       3.0Mi       228Mi       242Mi
Swap:             0B          0B          0B&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Hmmm, only &lt;span class="caps"&gt;242MB&lt;/span&gt; available. Okay, what&amp;#8217;s eating&amp;nbsp;it?&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;A quick clarification about &amp;#8220;free&amp;#8221; vs &amp;#8220;available&amp;#8221;. Ignore &amp;#8220;free&amp;#8221;. This is
the amount of &lt;span class="caps"&gt;RAM&lt;/span&gt; actually unused, but unused &lt;span class="caps"&gt;RAM&lt;/span&gt; is wasted &lt;span class="caps"&gt;RAM&lt;/span&gt;. The kernel
has a duty to minimize free &lt;span class="caps"&gt;RAM&lt;/span&gt; by filling anything unused with disk (and
other useful) caches which can be evicted trivially in the case that more
&lt;span class="caps"&gt;RAM&lt;/span&gt; is required. The &amp;#8220;available&amp;#8221; metric indicates the amount of &lt;span class="caps"&gt;RAM&lt;/span&gt; that is
available &amp;#8220;on demand&amp;#8221; should userland processes request it. It&amp;#8217;s &lt;em&gt;roughly&lt;/em&gt;
(but not precisely) equal to &amp;#8220;free&amp;#8221; +&amp;nbsp;&amp;#8220;buff/cache&amp;#8221;.&lt;/p&gt;
&lt;p class="last"&gt;Or to put it another way: &amp;#8220;available&amp;#8221; tells you how much &lt;span class="caps"&gt;RAM&lt;/span&gt; is available
for use; &amp;#8220;free&amp;#8221; is the metric which tells you how much &lt;span class="caps"&gt;RAM&lt;/span&gt; you&amp;#8217;ve wasted
your money on&amp;nbsp;;)&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We&amp;#8217;ll use the &lt;tt class="docutils literal"&gt;ps&lt;/tt&gt; command exclude everything under
&lt;span class="caps"&gt;PID&lt;/span&gt; 2 which is the kernel (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--ppid&lt;/span&gt; 2 &lt;span class="pre"&gt;-p&lt;/span&gt; 2 &lt;span class="pre"&gt;-N&lt;/span&gt;&lt;/tt&gt;). We&amp;#8217;ll turn on the
hierarchical view (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-H&lt;/span&gt;&lt;/tt&gt;) to get a clue of what belongs to what (this won&amp;#8217;t
show every inter-process dependency, but it can be a rough guide), and the
&amp;#8220;full&amp;#8221; output (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-F&lt;/span&gt;&lt;/tt&gt;) so we can get the memory usage info (amongst other
things; I&amp;#8217;ll exclude some of the columns below for the sake of text&amp;nbsp;wrapping):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;ps&lt;span class="w"&gt; &lt;/span&gt;--ppid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-NHF&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;UID          PID    PPID     SZ   RSS PSR TTY   CMD
root           1       0  41786 11364   2 ?     /sbin/init fixrtc splash
root         374       1  12020 12572   0 ?       /lib/systemd/systemd-journald
root         416       1  72416 25672   3 ?       /sbin/multipathd -d -s
root         429       1   5998  6332   2 ?       /lib/systemd/systemd-udevd
root         576       1   4087  9732   3 ?       /sbin/wpa_supplicant -c /run/netpla
systemd+     588       1  22160  6292   3 ?       /lib/systemd/systemd-timesyncd
systemd+     634       1   4097  7704   1 ?       /lib/systemd/systemd-networkd
systemd+     636       1   6270 12224   1 ?       /lib/systemd/systemd-resolved
message+     668       1   2280  4220   3 ?       &amp;#64;dbus-daemon --system --address=sys
root         673       1  20492  3344   2 ?       /usr/sbin/irqbalance --foreground
root         677       1   8217 17916   1 ?       /usr/bin/python3 /usr/bin/networkd-
root         678       1  59035  8220   3 ?       /usr/libexec/polkitd --no-debug
syslog       680       1  55508  4392   3 ?       /usr/sbin/rsyslogd -n -iNONE
root         684       1  366205 27060  0 ?       /usr/lib/snapd/snapd
root         687       1   6002  7164   2 ?       /lib/systemd/systemd-logind
root         691       1  98379 11280   3 ?       /usr/libexec/udisks2/udisksd
root         695       1   3813  3440   0 ?       /sbin/wpa_supplicant -u -s -O /run/
root         712       1   1727  2456   0 ?       /usr/sbin/cron -f -P
root         726       1  61827 12368   1 ?       /usr/sbin/ModemManager
root         736       1   1409   808   1 ttyS0   /sbin/agetty -o -p -- \u --keep-bau
root         744       1  27480 19884   0 ?       /usr/bin/python3 /usr/share/unatten
root         755       1   3787  8428   1 ?       sshd: /usr/sbin/sshd -D [listener]
root        1660     755   4530  9716   0 ?         sshd: ubuntu [priv]
ubuntu      1804    1660   4661  7592   1 ?           sshd: ubuntu&amp;#64;pts/0
ubuntu      1805    1804   2152  4852   2 pts/0         -bash
ubuntu    355020    1805   2495  2844   3 pts/0           ps --ppid 2 -p 2 -NHF
root         881       1    560   168   2 ?       /usr/bin/hciattach /dev/serial1 bcm
root         899       1   2386  4468   3 ?       /usr/lib/bluetooth/bluetoothd
ubuntu      1004       1   4559  9924   2 ?       /lib/systemd/systemd --user
ubuntu      1005    1004  42795  5800   2 ?         (sd-pam)
root        1766       1   1398   812   2 tty6    /sbin/agetty -o -p -- \u --noclear
root        1777       1   2417  3976   1 tty1    /bin/login -p --
ubuntu    227377    1777   2174  4856   2 tty1      -bash&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Okay, lots to go through here. &lt;span class="caps"&gt;RSS&lt;/span&gt; (Resident Set Size) is the most interesting
column here (it&amp;#8217;s &lt;a class="reference external" href="https://web.archive.org/web/20120520221529/http://emilics.com/blog/article/mconsumption.html"&gt;not wholly accurate&lt;/a&gt; but good enough for our purposes).
What&amp;#8217;s the fattest thing in &lt;span class="caps"&gt;RAM&lt;/span&gt;? Ah … snapd (at ~&lt;span class="caps"&gt;26MB&lt;/span&gt;). On my fat &lt;span class="caps"&gt;8GB&lt;/span&gt; Pi 4 I&amp;#8217;m
happy to leave that because I use the &lt;span class="caps"&gt;LXD&lt;/span&gt; snap (a lot!). However, this is a
little Zero 2W and I only need debs on this, so out it&amp;nbsp;goes:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;purge&lt;span class="w"&gt; &lt;/span&gt;snapd&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following package was automatically installed and is no longer required:
  squashfs-tools
Use 'sudo apt autoremove' to remove it.
The following packages will be REMOVED:
  snapd*
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 84.1 MB disk space will be freed.
Do you want to continue? [Y/n]
... lots of complaints about read-only stuff ...
rm: cannot remove '/snap/core20/1408/var/log': Read-only file system
rm: cannot remove '/snap/core20/1408/var/mail': Read-only file system
rm: cannot remove '/snap/core20/1408/var/opt': Read-only file system
rm: cannot remove '/snap/core20/1408/var/snap': Read-only file system
rm: cannot remove '/snap/core20/1408/var/spool/mail': Read-only file system
rm: cannot remove '/snap/core20/1408/var/tmp': Read-only file system
rm: cannot remove '/snap/core20/1408/writable': Read-only file system
Cannot remove directory /snap
Removing extra snap-confine apparmor rules
Removing snapd cache
Removing snapd state
dpkg: warning: while removing snapd, directory '/snap' not empty so not removed
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;autoremove&lt;span class="w"&gt; &lt;/span&gt;--purge&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages will be REMOVED:
  squashfs-tools*
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 408 kB disk space will be freed.
Do you want to continue? [Y/n]
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(Reading database ... 100500 files and directories currently installed.)&lt;/span&gt;
&lt;span class="go"&gt;Removing squashfs-tools (1:4.5-3build1) ...
Processing triggers for man-db (2.10.2-1) ...&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;At this point, given it couldn&amp;#8217;t remove everything under &lt;tt class="docutils literal"&gt;/snap&lt;/tt&gt; I&amp;#8217;d just
reboot to clear up any remaining mounts and &lt;tt class="docutils literal"&gt;sudo rm &lt;span class="pre"&gt;-fr&lt;/span&gt; /snap&lt;/tt&gt; (I don&amp;#8217;t
recall having to do this before; I&amp;#8217;ll try and remember to investigate this
further at some&amp;nbsp;point).&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;reboot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... wait to get back to the login ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-fr&lt;span class="w"&gt; &lt;/span&gt;/snap
&lt;/pre&gt;
&lt;p&gt;Now, what&amp;#8217;s next? &amp;#8220;multipathd&amp;#8221; (~&lt;span class="caps"&gt;25MB&lt;/span&gt;). This is a piece of the default server
installation for dealing with &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Multipath_I/O"&gt;multipath disk &lt;span class="caps"&gt;IO&lt;/span&gt;&lt;/a&gt;. However, unless you&amp;#8217;re
running a Compute Module with some fairly serious storage attached to it, this
is likely pointless on your Pi. Unfortunately, removing this is probably not a
good&amp;nbsp;idea:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;purge&lt;span class="w"&gt; &lt;/span&gt;multipath-tools&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages were automatically installed and are no longer required:
  kpartx libsgutils2-2 liburcu8 sg3-utils sg3-utils-udev
Use 'sudo apt autoremove' to remove them.
The following packages will be REMOVED:
  multipath-tools* ubuntu-server* ubuntu-server-raspi*
0 upgraded, 0 newly installed, 3 to remove and 0 not upgraded.
After this operation, 1179 kB disk space will be freed.
Do you want to continue? [Y/n] n
Abort.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Notice how it wanted to remove the &amp;#8220;ubuntu-server&amp;#8221; package? That&amp;#8217;s usually not
a good sign! However, perhaps we can disable it&amp;nbsp;somehow:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;multipathd.service&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;/lib/systemd/system/multipathd.service&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;[Unit]
Description=Device-Mapper Multipath Device Controller
Before=iscsi.service iscsid.service lvm2-activation-early.service
Before=local-fs-pre.target blk-availability.service shutdown.target
Wants=systemd-udevd-kernel.socket
After=systemd-udevd-kernel.socket
After=multipathd.socket systemd-remount-fs.service
DefaultDependencies=no
Conflicts=shutdown.target
ConditionKernelCommandLine=!nompath
ConditionKernelCommandLine=!multipath=off
ConditionVirtualization=!container

[Service]
... etc ...

[Install]
... etc ...&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Ah ha! Adding &lt;tt class="docutils literal"&gt;nompath&lt;/tt&gt; or the slightly more obvious &lt;tt class="docutils literal"&gt;multipath=off&lt;/tt&gt; to the
kernel command line should disable this service. Well, that&amp;#8217;s easily&amp;nbsp;accomplished:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s/$/ multipath=off/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;reboot
&lt;/pre&gt;
&lt;p&gt;I&amp;#8217;m sorely tempted to add this by default to the Pi images, but I need to dig a
bit further into whether multipath has any genuine use-cases on the&amp;nbsp;Pi.&lt;/p&gt;
&lt;p&gt;Next on the list? The Python-based &amp;#8220;unattended-upgrades-shutdown&amp;#8221; process
(~&lt;span class="caps"&gt;19MB&lt;/span&gt;). It&amp;#8217;s not a great idea to disable this (it ensures the computer doesn&amp;#8217;t
shut down in the middle of unattended-upgrades). However, it&amp;#8217;s annoying that it
hangs around the whole time eating &lt;span class="caps"&gt;RAM&lt;/span&gt; and only actually does anything at
shutdown. Still, it&amp;#8217;s the subject of an existing ticket, &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/unattended-upgrades/+bug/1955084"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1955084&lt;/a&gt; so
we&amp;#8217;ll just leave that one for&amp;nbsp;now.&lt;/p&gt;
&lt;p&gt;Next? Another Python process: the &amp;#8220;networkd-dispatcher&amp;#8221; daemon (~&lt;span class="caps"&gt;17MB&lt;/span&gt;). If you
like your WiFi connection, leave this one alone! Next up, it&amp;#8217;s
&amp;#8220;systemd-journald&amp;#8221; (~&lt;span class="caps"&gt;12MB&lt;/span&gt;). That&amp;#8217;s also doing useful work, so we&amp;#8217;ll leave that
one alone&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;Then … &amp;#8220;ModemManager&amp;#8221;. Hang on? &lt;em&gt;Modem&lt;/em&gt; Manager? Am I back in the 90s?! Oh,
it&amp;#8217;s for &lt;span class="caps"&gt;GSM&lt;/span&gt; modems. Still, I&amp;#8217;m not actually using a &lt;span class="caps"&gt;GSM&lt;/span&gt; modem on this Pi so
why is that there? In fact, it&amp;#8217;s the subject of another ticket, &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/ubuntu-meta/+bug/1981109"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1981109&lt;/a&gt;
(you may note it&amp;#8217;s another one filed by Steve, another colleague who has been
ruthlessly hunting down the cruft of late). Anyway, given the details in that
ticket this one can be safely excised (it&amp;#8217;s not even a real dependency, just a
&amp;#8220;recommendation&amp;#8221; of another&amp;nbsp;package)!&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;purge&lt;span class="w"&gt; &lt;/span&gt;modemmanager&lt;span class="w"&gt; &lt;/span&gt;--autoremove&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages will be REMOVED:
  libtcl8.6* modemmanager* tcl* tcl8.6* usb-modeswitch* usb-modeswitch-data*
0 upgraded, 0 newly installed, 6 to remove and 0 not upgraded.
After this operation, 8595 kB disk space will be freed.
Do you want to continue? [Y/n]
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(Reading database ... 100491 files and directories currently installed.)&lt;/span&gt;
&lt;span class="go"&gt;Removing usb-modeswitch (2.6.1-3ubuntu2) ...
Removing tcl (8.6.11+1build2) ...
Removing tcl8.6 (8.6.12+dfsg-1build1) ...
Removing libtcl8.6:arm64 (8.6.12+dfsg-1build1) ...
Removing modemmanager (1.18.6-1) ...
Unknown option: runtime
Removing usb-modeswitch-data (20191128-4) ...
Processing triggers for man-db (2.10.2-1) ...
Processing triggers for dbus (1.12.20-2ubuntu4) ...
Processing triggers for libc-bin (2.35-0ubuntu3) ...
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(Reading database ... 100105 files and directories currently installed.)&lt;/span&gt;
&lt;span class="go"&gt;Purging configuration files for tcl8.6 (8.6.12+dfsg-1build1) ...
Purging configuration files for usb-modeswitch (2.6.1-3ubuntu2) ...
Purging configuration files for modemmanager (1.18.6-1) ...
Processing triggers for dbus (1.12.20-2ubuntu4) ...&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Next up, &amp;#8220;systemd-resolved&amp;#8221; (~&lt;span class="caps"&gt;12MB&lt;/span&gt;). We shouldn&amp;#8217;t touch that one (&lt;span class="caps"&gt;DNS&lt;/span&gt;
resolution and caching is generally&amp;nbsp;useful).&lt;/p&gt;
&lt;p&gt;Next? &amp;#8220;udisks2&amp;#8221; (~&lt;span class="caps"&gt;11MB&lt;/span&gt;). This is a useful service, handling things like
auto-mounting &lt;span class="caps"&gt;USB&lt;/span&gt; storage devices that have been inserted. However, I&amp;#8217;m not
going to be doing that on this Pi Zero 2W so I don&amp;#8217;t need it running all the
time. I&amp;#8217;m not going to uninstall it as it&amp;#8217;s potentially useful and, even after
disabling it (which will prevent it auto-starting), the &lt;tt class="docutils literal"&gt;udisksctl&lt;/tt&gt; command
can implicitly start it back up again, should I find I need&amp;nbsp;it.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;udisks2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;disable&lt;span class="w"&gt; &lt;/span&gt;udisks2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Removed /etc/systemd/system/graphical.target.wants/udisks2.service&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;At this point we&amp;#8217;re into diminishing returns so let&amp;#8217;s reboot to a clean start
and see what &lt;tt class="docutils literal"&gt;free&lt;/tt&gt; says&amp;nbsp;now.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;reboot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... wait to get back to the login ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;               total        used        free      shared  buff/cache   available
Mem:           414Mi       103Mi       165Mi       2.0Mi       146Mi       299Mi
Swap:             0B          0B          0B&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s looking better! Nearly &lt;span class="caps"&gt;300MB&lt;/span&gt; free now. Finally, another tweak we can use
to grab a bit more memory. This one is only useful if you have no intention of
using &lt;span class="caps"&gt;GPU&lt;/span&gt; functionality (no camera, no video decoding / encoding acceleration,
etc). However, I don&amp;#8217;t need any graphics on this particular Pi (in fact there
won&amp;#8217;t be any display plugged into it) so let&amp;#8217;s reduce the &lt;span class="caps"&gt;GPU&lt;/span&gt; memory split all
the way down to the minimum (&lt;span class="caps"&gt;16MB&lt;/span&gt;).&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gpu_mem=16&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/config.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;reboot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... wait to get back to the login ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;               total        used        free      shared  buff/cache   available
Mem:           462Mi       101Mi       214Mi       2.0Mi       146Mi       348Mi
Swap:             0B          0B          0B&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;And there we have it; over &lt;span class="caps"&gt;100MB&lt;/span&gt; extra available memory from our starting
point. Bear in mind we have disabled some functionality to achieve this, so
these aren&amp;#8217;t all changes that could (or should!) be made universally. However,
from the tickets we&amp;#8217;ve encountered along the way there&amp;#8217;s definitely some
improvements that can still be made in the base&amp;nbsp;image.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="desktop"></category><category term="pi"></category><category term="jammy"></category></entry><entry><title>The Pi Zero 2</title><link href="https://waldorf.waveform.org.uk/2021/the-pi-zero-2.html" rel="alternate"></link><published>2021-10-28T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-10-28:/2021/the-pi-zero-2.html</id><summary type="html">&lt;p class="first last"&gt;How to run existing Ubuntu images at the launch of the Pi Zero&amp;nbsp;2&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A quick run-down of the new &lt;a class="reference external" href="https://www.raspberrypi.com/news/new-raspberry-pi-zero-2-w-2/"&gt;Raspberry Pi Zero 2&lt;/a&gt;, and how to run Ubuntu on it
(should you wish&amp;nbsp;to!).&lt;/p&gt;
&lt;div class="section" id="small-but-perfectly-formed"&gt;
&lt;h2&gt;Small, but perfectly&amp;nbsp;formed&lt;/h2&gt;
&lt;p&gt;While browsing the news last week I was particularly amused to
&lt;a class="reference external" href="https://www.raspberrypi.com/news/supply-chain-shortages-and-our-first-ever-price-increase/#comment-1564123"&gt;come&lt;/a&gt;
&lt;a class="reference external" href="https://www.raspberrypi.com/news/supply-chain-shortages-and-our-first-ever-price-increase/#comment-1564143"&gt;across&lt;/a&gt;
&lt;a class="reference external" href="https://www.raspberrypi.com/news/supply-chain-shortages-and-our-first-ever-price-increase/#comment-1564146"&gt;several&lt;/a&gt;
&lt;a class="reference external" href="https://www.raspberrypi.com/news/supply-chain-shortages-and-our-first-ever-price-increase/#comment-1564296"&gt;comments&lt;/a&gt;
on Eben&amp;#8217;s post about the first price increase of a Raspberry Pi in history
(&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt; &amp;#8220;&lt;a class="reference external" href="https://www.theregister.com/2021/10/21/raspberry_pi_price_rise/"&gt;global silicon shortage&lt;/a&gt; affects everyone, even Raspberry Pi&amp;#8221;), each
of which either speculated on designs for an updated Pi Zero, and/or the
potential time-frames we might be looking at for such a thing. I&amp;#8217;m sure each of
the erstwhile Cassandra&amp;#8217;s won&amp;#8217;t be disappointed to learn how far out their
predictions were!&amp;nbsp;:)&lt;/p&gt;
&lt;p&gt;I had to chuckle because for the better part of this year, I&amp;#8217;ve had a
production prototype of the Zero 2 lying somewhere on my desk for testing (and
have had to be careful to move it out of shot during certain video&amp;nbsp;calls!).&lt;/p&gt;
&lt;p&gt;The initial plan was to release Ubuntu Impish with support for the Zero 2.
However, as the global silicon shortage started to bite, the release date for
the Zero 2 slipped beyond Impish&amp;#8217;s release date, and the Impish release had to
go ahead without support for it on the&amp;nbsp;images.&lt;/p&gt;
&lt;p&gt;Speaking of which … what&amp;#8217;s actually required for support of the Zero 2? To
understand that, we should first take a look at the&amp;nbsp;hardware.&lt;/p&gt;
&lt;div class="section" id="zero-history"&gt;
&lt;h3&gt;Zero&amp;nbsp;History&lt;/h3&gt;
&lt;img alt="The original Pi Zero (top left), the Pi Zero rev 1.3 (top right) with added camera interface, the Pi Zero W (bottom left) with added WiFi and Bluetooth antenna, and the Pi Zero 2 (bottom right) with new SoC" src="https://waldorf.waveform.org.uk/images/zero2.jpg" /&gt;
&lt;p&gt;First, a quick visual tour of the Pi Zero&amp;#8217;s&amp;nbsp;history:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Top left is the original Pi Zero (board rev 1.2) with its 1GHz &lt;span class="caps"&gt;ARM6&lt;/span&gt; SoC, and
&lt;span class="caps"&gt;512MB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt; (the &lt;span class="caps"&gt;RAM&lt;/span&gt; is pretty much the only thing that hasn&amp;#8217;t changed in
terms of&amp;nbsp;specs!)&lt;/li&gt;
&lt;li&gt;Top right is the Pi Zero rev 1.3 which added the mini &lt;span class="caps"&gt;CSI&lt;/span&gt; connector for the
camera&amp;nbsp;interface&lt;/li&gt;
&lt;li&gt;Bottom left is the Pi Zero W which added the triangular Proant WiFi/&lt;span class="caps"&gt;BT&lt;/span&gt;
antenna and the WiFi chip (the shiny &lt;span class="caps"&gt;IC&lt;/span&gt; to the right of the&amp;nbsp;SoC)&lt;/li&gt;
&lt;li&gt;Finally, bottom right is the new Pi Zero 2 which replaced the venerable SoC
with the new quad-core &amp;#8220;&lt;span class="caps"&gt;RP3A0&lt;/span&gt;&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The form factor is obviously based on previous Raspberry Pi Zero models. The
major difference is that &lt;em&gt;huge&lt;/em&gt; (for the Pi Zero) SoC labelled the &amp;#8220;&lt;span class="caps"&gt;RP3A0&lt;/span&gt;&amp;#8221;
(and, slightly confusingly &amp;#8220;2041&amp;#8221; which is the manufacturing date and nothing
to do with &lt;span class="caps"&gt;RP2040&lt;/span&gt; on the &lt;a class="reference external" href="https://www.raspberrypi.com/products/raspberry-pi-pico/"&gt;Pico&lt;/a&gt;). It&amp;#8217;s the good ol&amp;#8217; 2710 (aka 2837) SoC that
powered the Raspberry Pi 3B, 3A+, and 3B+ packaged together with its &lt;span class="caps"&gt;RAM&lt;/span&gt; in
Raspberry Pi branded&amp;nbsp;silicon.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s pretty much the major headline right there: instead of the old
single-core &lt;span class="caps"&gt;ARM6&lt;/span&gt; chip that the Pi Zero shipped with, the new Zero 2 has a full
quad-core arm64-capable SoC. That also means it&amp;#8217;s the first Pi in a Zero
form-factor that Ubuntu&amp;#8217;s compatible with (in either armhf &lt;em&gt;or&lt;/em&gt; arm64&amp;nbsp;flavours).&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s also the notable WiFi &amp;#8220;can&amp;#8221; sitting to the right of the SoC, like the
3A+ and 3B+. However, it&amp;#8217;s worth noting that it &lt;em&gt;doesn&amp;#8217;t&lt;/em&gt; use the same WiFi
chip as the 3A+/3B+ (Broadcom 43455 compatible); instead it uses a WiFi
interface similar to the Pi 3B/Zero W (Broadcom 43430 compatible). Which just
means: don&amp;#8217;t go expecting 5GHz (it&amp;#8217;s 2.4GHz&amp;nbsp;only).&lt;/p&gt;
&lt;p&gt;If you were to think of it as a cheaper, smaller version of the 3B … well, you
wouldn&amp;#8217;t be far&amp;nbsp;wrong.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="ubuntu-impish"&gt;
&lt;h2&gt;Ubuntu&amp;nbsp;Impish&lt;/h2&gt;
&lt;p&gt;Ubuntu Impish &lt;strong&gt;armhf&lt;/strong&gt; (the 32-bit image) will support the Zero 2 &amp;#8220;out of the
box&amp;#8221; so you can just &lt;a class="reference external" href="https://ubuntu.com/download/raspberry-pi"&gt;flash&amp;#8217;n&amp;#8217;go&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;What about arm64? For that there&amp;#8217;s a wrinkle (on which, more details below):
you need an extra device-tree on the boot partition. On (or the day after? &lt;span class="caps"&gt;TBC&lt;/span&gt;)
release day, we should have a kernel update that includes the new Zero 2
device-tree, so the &amp;#8220;official&amp;#8221; method of upgrading a card to work on the Zero 2
should simply&amp;nbsp;be:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Boot your Ubuntu card on a supported Raspberry Pi (other than the Zero&amp;nbsp;2).&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sudo apt update&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sudo apt upgrade&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Shut down the Pi. Put the card in your new Zero 2 and boot&amp;nbsp;it!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What if you don&amp;#8217;t have a spare non-Zero 2 Raspberry Pi? In that case there&amp;#8217;s a
bit of a hack you can use instead (which I&amp;#8217;ve been using as a quick work-around
during&amp;nbsp;testing):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Flash a fresh Ubuntu Impish image to your choice of storage media (&lt;span class="caps"&gt;SD&lt;/span&gt; card,
&lt;span class="caps"&gt;SSD&lt;/span&gt;, etc). Probably the easiest means of doing this is the &lt;a class="reference external" href="http://rptl.io/imager"&gt;Raspberry Pi
Imaging utility&lt;/a&gt; (you&amp;#8217;ll find Ubuntu under &amp;#8220;Other general purpose &lt;span class="caps"&gt;OS&lt;/span&gt;&amp;#8221;).&lt;/li&gt;
&lt;li&gt;Once complete, safely remove the medium and re-insert. The boot partition
(and potentially the root partition if you&amp;#8217;re on Linux) will&amp;nbsp;auto-mount.&lt;/li&gt;
&lt;li&gt;Open the boot partition in your file-browser and find the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;bcm2710-rpi-3-b.dtb&lt;/span&gt;&lt;/tt&gt; file. Make a copy of this file and rename it to
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;bcm2710-rpi-zero-2.dtb&lt;/span&gt;&lt;/tt&gt;. The trick here is simply that the Zero 2&amp;#8217;s
hardware is &lt;em&gt;close enough&lt;/em&gt; to the 3B (minus the ethernet port, some &lt;span class="caps"&gt;RAM&lt;/span&gt;, and
the &lt;span class="caps"&gt;DSI&lt;/span&gt; port) that the 3B&amp;#8217;s device-tree&amp;nbsp;works.&lt;/li&gt;
&lt;li&gt;You&amp;#8217;re done! You can now boot the card on your new Zero 2. Once you&amp;#8217;ve done
that, go through the usual &lt;tt class="docutils literal"&gt;sudo apt update&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;sudo apt upgrade&lt;/tt&gt; dance to
get the &amp;#8220;proper&amp;#8221; device-tree&amp;nbsp;installed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="ubuntu-focal"&gt;
&lt;h2&gt;Ubuntu&amp;nbsp;Focal&lt;/h2&gt;
&lt;p&gt;Ubuntu Focal, as the current &lt;span class="caps"&gt;LTS&lt;/span&gt;, will be receiving support for the Pi Zero 2 in
the next kernel &lt;span class="caps"&gt;SRU&lt;/span&gt; cycle which is due out on Monday, November 8th. Once that
lands, you can do the &lt;tt class="docutils literal"&gt;sudo apt update&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;sudo apt upgrade&lt;/tt&gt; dance on
another (non-Zero 2) Raspberry Pi to upgrade your card for compatibility with
the Zero&amp;nbsp;2.&lt;/p&gt;
&lt;p&gt;What if you don&amp;#8217;t have another Pi? That gets a bit more tricky. First you have
to &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/you-boot-no-u-boot-first.html"&gt;get rid of u-boot&lt;/a&gt; from the boot sequence, &lt;em&gt;then&lt;/em&gt; copy the 3B dtb as for
Impish above. Once that&amp;#8217;s done Focal seems to work reasonably happily (though I
haven&amp;#8217;t had time to do extensive testing, just make sure the basics like &lt;span class="caps"&gt;USB&lt;/span&gt;
and WiFi are&amp;nbsp;operating).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pieces-of-82"&gt;
&lt;h2&gt;Pieces of&amp;nbsp;8²&lt;/h2&gt;
&lt;p&gt;Why isn&amp;#8217;t all this copying of device trees necessary on the armhf image for
Ubuntu Impish? It&amp;#8217;s a funny story&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;In order to get the Raspberry Pi Zero 2 to boot successfully (or any Pi for
that matter), you need a device-tree for the board. That&amp;#8217;s what all those
&amp;#8220;.dtb&amp;#8221; files are on the boot partition. When the Pi starts up, the boot
firmware checks what board it&amp;#8217;s on, finds the appropriate device-tree file and
loads it. The device-tree in turn tells the kernel what devices exist, what
interfaces they&amp;#8217;re connected to, bus addresses, etc.&amp;nbsp;etc.&lt;/p&gt;
&lt;p&gt;Obviously the device-tree for the Zero 2 has to be named
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;bcm2710-rpi-zero-2.dtb&lt;/span&gt;&lt;/tt&gt;. But that file doesn&amp;#8217;t exist on the armhf image, so
how can it&amp;nbsp;boot?&lt;/p&gt;
&lt;p&gt;In order to permit early testing, the Raspberry Pi developers sneaked in a hack
of their own to the boot firmware. If the firmware finds itself running on a
Zero 2, and also finds a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;bcm2708-rpi-zero-w.dtb&lt;/span&gt;&lt;/tt&gt; (for the Raspberry Pi Zero
W), it loads &lt;em&gt;that&lt;/em&gt; then patches it to be equivalent to the appropriate Zero 2
device-tree. As it happens, despite not supporting the Zero W (which has an
&lt;span class="caps"&gt;ARM6&lt;/span&gt; processor; Ubuntu only supports the &lt;span class="caps"&gt;ARM7&lt;/span&gt; arch and upward), our armhf
images have always shipped with a (redundant) &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;bcm2708-rpi-zero-w.dtb&lt;/span&gt;&lt;/tt&gt; on the
boot partition so by happy coincidence they &amp;#8220;just&amp;nbsp;work&amp;#8221;.&lt;/p&gt;
&lt;p&gt;However, the arm64 images &lt;em&gt;didn&amp;#8217;t&lt;/em&gt; ship with a (redundant) Zero W device-tree.
And naturally, adding one would&amp;#8217;ve looked … somewhat suspicious (&amp;#8220;so Dave, why
&lt;em&gt;are&lt;/em&gt; you adding a device-tree for a board you definitely &lt;em&gt;don&amp;#8217;t&lt;/em&gt; support on
&lt;em&gt;any&lt;/em&gt; architecture?&amp;#8221;), and obviously we couldn&amp;#8217;t just shove a device-tree named
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;bcm2710-rpi-zero-2.dtb&lt;/span&gt;&lt;/tt&gt; on the boot partition without giving the whole thing
away (and probably violating various&amp;nbsp;NDAs)!&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;img alt="A frame from Futurama's &amp;quot;The Lesser of Two Evils&amp;quot; depicting Fry's eyes narrowing, frequently (possibly overly) used as a meme denoting suspicion." src="https://waldorf.waveform.org.uk/images/suspicious.gif" /&gt;
&lt;p class="caption"&gt;Fry from Futurama looks suspiciously at the new device-tree on the
boot&amp;nbsp;partition&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Anyway, the 3B device-tree is a reasonably close match to the Zero 2&amp;#8217;s
hardware, and is sufficient for it to boot without further alteration. The only
thing I&amp;#8217;ve noticed that&amp;#8217;s &amp;#8220;off&amp;#8221; when the 3B device-tree is in use is that
&lt;tt class="docutils literal"&gt;/proc/asound/cards&lt;/tt&gt; reports both the &lt;span class="caps"&gt;HDMI&lt;/span&gt; audio output, and the auxiliary
headphone socket (the latter doesn&amp;#8217;t exist on the Zero 2, and doesn&amp;#8217;t appear
when the &amp;#8220;official&amp;#8221; device-tree is in&amp;nbsp;use).&lt;/p&gt;
&lt;p&gt;And that&amp;#8217;s all folks! Now, go treat yourself to the newest diminutive member of
the ever-growing Pi family, and have some&amp;nbsp;fun!&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="zero"></category><category term="ubuntu"></category></entry><entry><title>Playing with Blocks - LUKS</title><link href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html" rel="alternate"></link><published>2021-10-19T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-10-19:/2021/playing-with-blocks-luks.html</id><summary type="html">&lt;p class="first last"&gt;Making a Pi desktop with encrypted root&amp;nbsp;storage&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2022/playing-with-blocks-its-all-connected.html"&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;+&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;Last time&lt;/a&gt; we had a look at using &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)"&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;&lt;/a&gt; on our root storage. This time
around it&amp;#8217;s &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup"&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/a&gt;&amp;#8216; turn: we&amp;#8217;re going to make an Ubuntu Desktop for Raspberry
Pi card, with the root file-system encrypted with &lt;span class="caps"&gt;LUKS&lt;/span&gt;. Thankfully this is much
simpler affair as &lt;span class="caps"&gt;LUKS&lt;/span&gt; can encrypt, decrypt, or for that matter, re-encrypt a
block device non-destructively (even with interruptions in the&amp;nbsp;middle!).&lt;/p&gt;
&lt;div class="section" id="as-easy-as"&gt;
&lt;h2&gt;As Easy As&amp;nbsp;…&lt;/h2&gt;
&lt;p&gt;We get off to the simplest possible start. You&amp;#8217;ll need a machine running Ubuntu
and some form of Pi-compatible storage (an &lt;span class="caps"&gt;SD&lt;/span&gt; card, or some form of &lt;span class="caps"&gt;USB&lt;/span&gt; mass&amp;nbsp;storage).&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Install the &lt;a class="reference external" href="https://www.raspberrypi.com/software/"&gt;Raspberry Pi imaging utility&lt;/a&gt; (or use &lt;tt class="docutils literal"&gt;sudo snap install
&lt;span class="pre"&gt;rpi-imager&lt;/span&gt;&lt;/tt&gt; for the snap version if you&amp;#8217;re on an architecture other than&amp;nbsp;amd64)&lt;/li&gt;
&lt;li&gt;Grab an &lt;span class="caps"&gt;SD&lt;/span&gt; card or some form of &lt;span class="caps"&gt;USB&lt;/span&gt; storage (if you&amp;#8217;re intending to use the
desktop image, I&amp;#8217;d recommend something fast like an &lt;span class="caps"&gt;SSD&lt;/span&gt;)&lt;/li&gt;
&lt;li&gt;Install the Ubuntu 21.10 desktop image (substitute server if you wish) on the
storage with the imaging utility (I won&amp;#8217;t bother detailing this process
because, frankly, the pi&amp;#8217;s imaging utility is about as simple as you can
possibly&amp;nbsp;get!)&lt;/li&gt;
&lt;li&gt;Boot the installed image on your&amp;nbsp;Pi&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="wot-no-cryptsetup"&gt;
&lt;h2&gt;Wot, no&amp;nbsp;cryptsetup?&lt;/h2&gt;
&lt;p&gt;It might seem a bit odd that we&amp;#8217;re jumping straight to booting the image, but
there is a reason for this: on earlier versions of Ubuntu, the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cryptsetup-initramfs&lt;/span&gt;&lt;/tt&gt; package isn&amp;#8217;t installed by default. On Impish it &lt;em&gt;is&lt;/em&gt;
installed by default, but the initramfs that ships with the image still doesn&amp;#8217;t
incorporate the &lt;tt class="docutils literal"&gt;cryptsetup&lt;/tt&gt; binary that&amp;#8217;s actually necessary. Hence, we need
to boot it at least once and re-generate the initramfs to include&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Get to a usable terminal (if you&amp;#8217;re on the desktop image just go through the
initial setup), then install &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cryptsetup-initramfs&lt;/span&gt;&lt;/tt&gt; (as mentioned, on Impish
this shouldn&amp;#8217;t be necessary but best to make sure) and re-generate the&amp;nbsp;initramfs.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;cryptsetup-initramfs&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... usual apt stuff ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;update-initramfs&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... lots of flash-kernel output ...&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;That last step will take several minutes; here&amp;#8217;s &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/making-impish-impier.html"&gt;some hints&lt;/a&gt; to read first if
you want to speed things up a bit. Now shut down the Pi and plug the &lt;span class="caps"&gt;SD&lt;/span&gt; card
(or &lt;span class="caps"&gt;USB&lt;/span&gt; drive) back into your original&amp;nbsp;machine.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="setec-astronomy"&gt;
&lt;h2&gt;Setec&amp;nbsp;Astronomy&lt;/h2&gt;
&lt;p&gt;Time to perform the actual encryption. Below I&amp;#8217;m assuming that your &lt;span class="caps"&gt;SD&lt;/span&gt; card (or
whatever target storage you&amp;#8217;re using) appears as the &lt;tt class="docutils literal"&gt;/dev/mmcblk0&lt;/tt&gt; on your
machine. Adjust these instructions according to your&amp;nbsp;setup!&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re going to unmount the partitions of the &lt;span class="caps"&gt;SD&lt;/span&gt; card (or other storage) you&amp;#8217;ve
just inserted (this is only necessary if your Ubuntu machine is a desktop
machine which will auto-mount the card), force a file-system check of the root
partition, and shrink that partition to its minimal&amp;nbsp;size:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p1&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;fsck&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;fsck from util-linux 2.34
e2fsck 1.45.5 (07-Jan-2020)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
writable: 154688/557520 files (0.2% non-contiguous), 1507014/2229303 blocks
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;resize2fs&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;resize2fs 1.45.5 (07-Jan-2020)
Resizing the filesystem on /dev/sda2 to 1881115 (4k) blocks.
The filesystem on /dev/sda2 is now 1881115 (4k) blocks long.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Now for the actual encryption. First, pick a &lt;a class="reference external" href="https://xkcd.com/936/"&gt;decent passphrase&lt;/a&gt;, then pick a
decent encryption cipher. Below I&amp;#8217;m going to use the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Adiantum_(cipher)"&gt;Adiantum cipher&lt;/a&gt; as this
performs reasonably well on the Pi. It&amp;#8217;s well above the speed of most &lt;span class="caps"&gt;SD&lt;/span&gt; cards,
but be aware it will probably hurt the performance of SSDs. Use &lt;tt class="docutils literal"&gt;cryptsetup
benchmark &lt;span class="pre"&gt;--cipher&lt;/span&gt; &lt;span class="pre"&gt;xchacha12,aes-adiantum&lt;/span&gt;&lt;/tt&gt; if you want an idea of its
performance; compare this to the output of &lt;tt class="docutils literal"&gt;cryptsetup benchmark&lt;/tt&gt; which runs
through several of the more common&amp;nbsp;algorithms.&lt;/p&gt;
&lt;p&gt;At any rate, expect this next bit to take some considerable&amp;nbsp;time!&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;reencrypt&lt;span class="w"&gt; &lt;/span&gt;--encrypt&lt;span class="w"&gt; &lt;/span&gt;--reduce-device-size&lt;span class="w"&gt; &lt;/span&gt;16M&lt;span class="w"&gt; &lt;/span&gt;--cipher&lt;span class="w"&gt; &lt;/span&gt;xchacha12,aes-adiantum-plain64&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
WARNING!
========
This will overwrite data on LUKS2-temp-dd54103b-d96c-4d1d-9e23-e3c375c81ecb.new irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase for LUKS2-temp-dd54103b-d96c-4d1d-9e23-e3c375c81ecb.new:
Verify passphrase:
Progress:   1.8%, ETA 09:51,  160 MiB written, speed  14.4 MiB/s
...
Progress:  17.7%, ETA 08:05, 1544 MiB written, speed  14.8 MiB/s
...
Finished, time 09:45.528, 8700 MiB written, speed  14.8 MiB/s
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;open&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt; &lt;/span&gt;rootfs&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Enter passphrase for /dev/mmcblk0p2:&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="rooting-around"&gt;
&lt;h2&gt;Rooting&amp;nbsp;Around&lt;/h2&gt;
&lt;p&gt;At this point our encrypted container on &lt;tt class="docutils literal"&gt;/dev/mmcblk0p2&lt;/tt&gt; is open as a
decrypted device called &lt;tt class="docutils literal"&gt;/dev/mapper/rootfs&lt;/tt&gt;. We can now expand the partition
back to its &amp;#8220;normal&amp;#8221; size (minus the 16M we shrunk it for metadata), and mount
it (and the boot partition) at some appropriate&amp;nbsp;location.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;resize2fs&lt;span class="w"&gt; &lt;/span&gt;/dev/mapper/rootfs&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;resize2fs 1.45.5 (07-Jan-2020)
Resizing the filesystem on /dev/mapper/rootfs to 2227255 (4k) blocks.
The filesystem on /dev/mapper/rootfs is now 2227255 (4k) blocks long.
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p1&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/mapper/rootfs&lt;span class="w"&gt; &lt;/span&gt;/mnt/root
&lt;/pre&gt;
&lt;p&gt;Now we need to perform some surgery on the file-system tables. Firstly, we need
to tell the system how to set up the &amp;#8220;rootfs&amp;#8221; node we just opened. This is the
job of the &lt;tt class="docutils literal"&gt;/etc/crypttab&lt;/tt&gt; file. This should look relatively familiar to
anyone used to messing with &lt;tt class="docutils literal"&gt;/etc/fstab&lt;/tt&gt;, but bear in mind that the target
and source devices are &amp;#8220;backwards&amp;#8221; (target first, then&amp;nbsp;source).&lt;/p&gt;
&lt;p&gt;Another &lt;strong&gt;important&lt;/strong&gt; thing to remember here is that in this file,
&lt;tt class="docutils literal"&gt;/dev/mmcblk0p2&lt;/tt&gt; is the device for the encrypted partition &lt;em&gt;from the Pi&amp;#8217;s
perspective&lt;/em&gt;. In other words, if your &lt;span class="caps"&gt;SD&lt;/span&gt; card appears as &lt;tt class="docutils literal"&gt;/dev/sdf&lt;/tt&gt; on your
&lt;span class="caps"&gt;PC&lt;/span&gt; and you&amp;#8217;ve been using &lt;tt class="docutils literal"&gt;/dev/sdf2&lt;/tt&gt; as the card&amp;#8217;s root partition in the
instructions above you should &lt;em&gt;not&lt;/em&gt; do that here: just use &lt;tt class="docutils literal"&gt;/dev/mmcblk0p2&lt;/tt&gt;.
However, if you&amp;#8217;re using an &lt;span class="caps"&gt;SSD&lt;/span&gt; via a &lt;span class="caps"&gt;USB&lt;/span&gt; adapter as your storage, you may need
to adjust the line below to use &lt;tt class="docutils literal"&gt;/dev/sda2&lt;/tt&gt; as that&amp;#8217;s how the Pi will be
referring to this partition at boot&amp;nbsp;time.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&amp;lt;target&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;device&amp;gt;&lt;span class="w"&gt;         &lt;/span&gt;&amp;lt;key&lt;span class="w"&gt; &lt;/span&gt;file&amp;gt;&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;options&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rootfs  /dev/mmcblk0p2  none  luks,discard&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&amp;lt;target&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;device&amp;gt;&lt;span class="w"&gt;         &lt;/span&gt;&amp;lt;key&lt;span class="w"&gt; &lt;/span&gt;file&amp;gt;&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;options&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;rootfs  /dev/mmcblk0p2  none  luks,discard&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;You may or may not wish to include the &amp;#8220;discard&amp;#8221; option above. It&amp;#8217;s the
default in Debian and Ubuntu for encrypted partitions but may carry
security risks. See &lt;tt class="docutils literal"&gt;man crypttab&lt;/tt&gt; for full&amp;nbsp;details.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;tt class="docutils literal"&gt;/dev/mapper/rootfs&lt;/tt&gt; device configured, all that remains is to tell
the kernel and systemd to look there for its root device via the &lt;tt class="docutils literal"&gt;/etc/fstab&lt;/tt&gt;
table, and the &lt;tt class="docutils literal"&gt;cmdline.txt&lt;/tt&gt; file on the boot&amp;nbsp;partition:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;UNCONFIGURED&lt;span class="w"&gt; &lt;/span&gt;FSTAB&lt;span class="w"&gt; &lt;/span&gt;FOR&lt;span class="w"&gt; &lt;/span&gt;BASE&lt;span class="w"&gt; &lt;/span&gt;SYSTEM&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;LABEL=writable    /     ext4    defaults,x-systemd.growfs    0 0
/swapfile         none  swap    sw    0 0
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s,^LABEL=writable,/dev/mapper/rootfs,'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;UNCONFIGURED&lt;span class="w"&gt; &lt;/span&gt;FSTAB&lt;span class="w"&gt; &lt;/span&gt;FOR&lt;span class="w"&gt; &lt;/span&gt;BASE&lt;span class="w"&gt; &lt;/span&gt;SYSTEM&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;/dev/mapper/rootfs    /     ext4    defaults,x-systemd.growfs    0 0
/swapfile         none  swap    sw    0 0
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc quiet splash
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s,LABEL=writable,/dev/mapper/rootfs,'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;dwc_otg.lpm_enable=0 console=tty1 root=/dev/mapper/rootfs rootfstype=ext4 elevator=deadline rootwait fixrtc quiet splash&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Again, feel free to use an editor here instead of messing around with
&lt;tt class="docutils literal"&gt;sed&lt;/tt&gt;; I&amp;#8217;ve illustrated the changes necessary by showing the before and
after states of the file&amp;nbsp;affected.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="fail-and-then-succeed"&gt;
&lt;h2&gt;Fail! (and then&amp;nbsp;succeed)&lt;/h2&gt;
&lt;p&gt;Time to clean up and test our&amp;nbsp;card.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;close&lt;span class="w"&gt; &lt;/span&gt;rootfs
&lt;/pre&gt;
&lt;p&gt;Eject the card (or mass storage) properly, shove it in your Pi and start it
booting. This first boot will appear to take a &lt;em&gt;very&lt;/em&gt; long time (several
minutes, while it times out waiting for the root device to appear) and will
then fail, dropping to the initramfs prompt. The reason for this is that, even
though we re-generated the initramfs to include the &lt;tt class="docutils literal"&gt;cryptsetup&lt;/tt&gt; tool, it
still isn&amp;#8217;t configured to actually unlock the correct partition. We&amp;#8217;ll need to
re-generate it again to do that, but first we need to get the system&amp;nbsp;booted.&lt;/p&gt;
&lt;p&gt;At the &lt;tt class="docutils literal"&gt;(initramfs)&lt;/tt&gt; prompt run the following to get the system booting
(if you&amp;#8217;re booting an &lt;span class="caps"&gt;SSD&lt;/span&gt; drive, adjust &lt;tt class="docutils literal"&gt;/dev/mmcblk0p2&lt;/tt&gt; below to
&lt;tt class="docutils literal"&gt;/dev/sda2&lt;/tt&gt;):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp-VirtualEnv"&gt;(initramfs)&lt;/span&gt; &lt;span class="go"&gt;cryptsetup open /dev/mmcblk0p2 rootfs
Enter passphrase for /dev/mmcblk0p2:
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(initramfs)&lt;/span&gt; &lt;span class="go"&gt;exit&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Once the system has booted successfully, login and re-generate the initramfs
one last time before rebooting to test your new&amp;nbsp;setup:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;update-initramfs&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;reboot
&lt;/pre&gt;
&lt;p&gt;This time the initramfs should prompt you nicely to unlock your device before
proceeding with the rest of the&amp;nbsp;boot.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s all for this time. Let me know below if you run into any issues with&amp;nbsp;this!&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="ubuntu"></category><category term="storage"></category><category term="luks"></category></entry><entry><title>Playing with Blocks - LVM</title><link href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html" rel="alternate"></link><published>2021-10-18T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-10-18:/2021/playing-with-blocks-lvm.html</id><summary type="html">&lt;p class="first last"&gt;Making a Pi server with root under &lt;span class="caps"&gt;LVM&lt;/span&gt;&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&amp;nbsp;encryption&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is the first of a few posts I&amp;#8217;ll be doing on customizing the storage of
Ubuntu for Raspberry Pi&amp;nbsp;images.&lt;/p&gt;
&lt;p&gt;One of the first things I did when I moved to using a Pi as my primary
workstation was set up &lt;a class="reference external" href="https://linuxcontainers.org/lxd/introduction/"&gt;&lt;span class="caps"&gt;LXD&lt;/span&gt;&lt;/a&gt; to enable &amp;#8220;clean&amp;#8221; build environments to help with
debugging things. My favoured &lt;span class="caps"&gt;LXD&lt;/span&gt; storage backend is &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)"&gt;lvm&lt;/a&gt;&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;(cue gasps of horror, followed by overlapping cries of &amp;#8220;but &lt;a class="reference external" href="https://en.wikipedia.org/wiki/ZFS"&gt;zfs&lt;/a&gt; rules!&amp;#8221; and
&amp;#8220;what about &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Btrfs"&gt;btrfs&lt;/a&gt;?&amp;#8221;, before the two sides descend into several parallel
flame wars over licensing and reliability and performance and … well, you get
the&amp;nbsp;point)&lt;/p&gt;
&lt;p&gt;Yes, I like lvm. Which means I&amp;#8217;d really like lvm on my Pi&amp;#8217;s &lt;span class="caps"&gt;SSD&lt;/span&gt;, preferably for
everything except the boot partition (which can&amp;#8217;t be lvm because the bootloader
doesn&amp;#8217;t handle esoterica like&amp;nbsp;that).&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m going to demonstrate the necessary steps with a fresh Ubuntu 21.10 (Impish
Indri) image on an &lt;span class="caps"&gt;SD&lt;/span&gt; card, but you can adapt the steps trivially to target an
&lt;span class="caps"&gt;SSD&lt;/span&gt; drive on &lt;span class="caps"&gt;USB&lt;/span&gt; (just substitute &lt;tt class="docutils literal"&gt;/dev/sdX&lt;/tt&gt; wherever you see
&lt;tt class="docutils literal"&gt;/dev/mmcblkX&lt;/tt&gt; below). With a teensy bit more effort, you could also operate
on an existing installation rather than a fresh image (but again, you&amp;#8217;ll need
separate target storage &amp;#8212; you can&amp;#8217;t trivially convert an existing installation
to lvm &lt;em&gt;on the same block device&lt;/em&gt;).&lt;/p&gt;
&lt;div class="section" id="tabula-rasa"&gt;
&lt;h2&gt;Tabula&amp;nbsp;Rasa&lt;/h2&gt;
&lt;p&gt;Start by grabbing your fresh &lt;span class="caps"&gt;SD&lt;/span&gt; card (or whatever storage you prefer). A word
of (frankly obvious) warning here: we&amp;#8217;re going to wipe everything on this
storage so make sure you&amp;#8217;re not going to mind losing anything on there&amp;nbsp;first.&lt;/p&gt;
&lt;p&gt;Instead of flashing an entire image to it, we&amp;#8217;re going to set it up with our
own partition table, create an &lt;span class="caps"&gt;LVM&lt;/span&gt; layout within that, then flash each
&lt;em&gt;partition&lt;/em&gt; of the image to it separately (all two of them, that&amp;nbsp;is).&lt;/p&gt;
&lt;p&gt;First things first: creating the new partition table. Plug in your storage, in
my case an &lt;span class="caps"&gt;SD&lt;/span&gt; card. If you&amp;#8217;re on an Ubuntu desktop (as I am) you&amp;#8217;ll need to
unmount any partitions that auto-mount from this storage before proceeding to
(re-)partition&amp;nbsp;it.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p1&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... repeat for any more auto-mounted partitions ...&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Now we&amp;#8217;re going to partition our storage with &lt;span class="caps"&gt;GPT&lt;/span&gt; (plus protective &lt;span class="caps"&gt;MBR&lt;/span&gt;), set
up the first partition as &lt;span class="caps"&gt;FAT&lt;/span&gt; (for the bootloader bits), and the second
partition as &lt;span class="caps"&gt;LVM&lt;/span&gt;. The commands we&amp;#8217;ll execute are as&amp;nbsp;follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;(&lt;strong&gt;p&lt;/strong&gt;)rint the current state of the partition table (not necessary, but just
to be sure we&amp;#8217;re looking at the &amp;#8220;right&amp;#8221;&amp;nbsp;disk)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;o&lt;/strong&gt;)verwrite the partition table (to wipe it and start&amp;nbsp;again)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;n&lt;/strong&gt;)ew partition, with the default number (1), default start sector
(2048), a size of &lt;span class="caps"&gt;512MB&lt;/span&gt; (actually larger than the source image which is
&lt;span class="caps"&gt;256MB&lt;/span&gt;, but that always feels a bit tight to me), and partition type &lt;tt class="docutils literal"&gt;0700&lt;/tt&gt;
(Microsoft basic&amp;nbsp;data)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;n&lt;/strong&gt;)ew partition again, with the default number (2), default start sector
(1050624), default size (rest of the storage medium, however large that is),
and partition type of &lt;tt class="docutils literal"&gt;8e00&lt;/tt&gt; (Linux &lt;span class="caps"&gt;LVM&lt;/span&gt;)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;c&lt;/strong&gt;)hange partition 1&amp;#8217;s name to&amp;nbsp;&amp;#8220;system-boot&amp;#8221;&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;c&lt;/strong&gt;)hange partition 2&amp;#8217;s name to&amp;nbsp;&amp;#8220;lvm&amp;#8221;&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;p&lt;/strong&gt;)rint the current state of the partition table again (not necessary,
but just to confirm things &amp;#8220;look&amp;nbsp;right&amp;#8221;)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;w&lt;/strong&gt;)rite the new partition table and exit (confirming we want to&amp;nbsp;proceed)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;#8217;s a transcript of me doing this with a &lt;span class="caps"&gt;16GB&lt;/span&gt; &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;gdisk&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;GPT fdisk (gdisk) version 1.0.5

Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present


***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by
typing 'q' if you don't want to convert your MBR partitions
to GPT format!
***************************************************************


Command (? for help): p
Disk /dev/mmcblk0: 31116288 sectors, 14.8 GiB
Model: STORAGE DEVICE
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 30D2AB17-E36C-4915-AF37-2C94E8FFE5C4
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 31116254
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          526335   256.0 MiB   0700  Microsoft basic data
   2          526336        31116254   14.6 GiB    8300  Linux filesystem

Command (? for help): o
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): y

Command (? for help): n
Partition number (1-128, default 1):
First sector (34-31116254, default = 2048) or {+-}size{KMGTP}:
Last sector (2048-31116254, default = 31116254) or {+-}size{KMGTP}: 512M
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 0700
Changed type of partition to 'Microsoft basic data'

Command (? for help): n
Partition number (2-128, default 2):
First sector (34-31116254, default = 1050624) or {+-}size{KMGTP}:
Last sector (1050624-31116254, default = 31116254) or {+-}size{KMGTP}:
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8e00
Changed type of partition to 'Linux LVM'

Command (? for help): c
Partition number (1-2): 1
Enter name: system-boot

Command (? for help): c
Partition number (1-2): 2
Enter name: lvm

Command (? for help): p
Disk /dev/mmcblk0: 31116288 sectors, 14.8 GiB
Model: STORAGE DEVICE
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): C202E4F2-59CD-402D-9720-3C8D7D0E84AF
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 31116254
Partitions will be aligned on 2048-sector boundaries
Total free space is 4061 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1048576   511.0 MiB   0700  system-boot
   2         1050624        31116254   14.3 GiB    8E00  lvm

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/mmcblk0.
The operation has completed successfully.&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;You may note we&amp;#8217;re using &lt;span class="caps"&gt;GPT&lt;/span&gt; partitioning above. This is okay provided
you&amp;#8217;re using a reasonably modern version of the Pi&amp;#8217;s bootloader which has
supported &lt;span class="caps"&gt;GPT&lt;/span&gt; partitioning since mid-2020. In other words, this should work
with post-Focal images (including later Focal point-releases) but don&amp;#8217;t try
it with anything older than&amp;nbsp;that.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="more-logical-volume"&gt;
&lt;h2&gt;More (logical)&amp;nbsp;volume!&lt;/h2&gt;
&lt;p&gt;Now we set up the logical volume(s) on our &lt;span class="caps"&gt;GPT&lt;/span&gt; partitioned&amp;nbsp;card.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pvcreate&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Physical volume &amp;quot;/dev/mmcblk0p2&amp;quot; successfully created.
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vgcreate&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Volume group &amp;quot;pivg&amp;quot; successfully created
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;lvcreate&lt;span class="w"&gt; &lt;/span&gt;--size&lt;span class="w"&gt; &lt;/span&gt;8G&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Logical volume &amp;quot;root&amp;quot; created.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You might want to make the root &lt;span class="caps"&gt;LV&lt;/span&gt; bigger than 8G, especially if you&amp;#8217;re using
the desktop image. I&amp;#8217;m specifically not using all the available storage (&lt;span class="caps"&gt;16GB&lt;/span&gt;
in this case) because I assume you want some spare for … whatever it is you
want &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)"&gt;lvm&lt;/a&gt; (in my case, a thin pool for &lt;span class="caps"&gt;LXD&lt;/span&gt;&amp;nbsp;containers).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="going-loopy"&gt;
&lt;h2&gt;Going&amp;nbsp;loopy&lt;/h2&gt;
&lt;p&gt;Time to grab a source image. Below, I&amp;#8217;m getting a copy of the &lt;a class="reference external" href="https://ubuntu.com/download/raspberry-pi"&gt;Ubuntu 21.10&lt;/a&gt;
pre-installed server image for the Pi (substitute the desktop image here if you
like, there&amp;#8217;s no real difference in the procedure), and unpacking it with the
&lt;tt class="docutils literal"&gt;unxz&lt;/tt&gt; utility:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;xz-utils&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... all the usual apt stuff ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;http://cdimage.ubuntu.com/releases/impish/release/ubuntu-21.10-preinstalled-server-arm64+raspi.img.xz&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;unxz&lt;span class="w"&gt; &lt;/span&gt;ubuntu-21.10-preinstalled-server-arm64+raspi.img.xz
&lt;/pre&gt;
&lt;p&gt;We need to access the individual &lt;em&gt;partitions&lt;/em&gt; on the original image as we need
to copy its boot partition to our new boot partition, then its root partition
to our new root logical volume. To do this we set up a loop-device which treats
our unpacked image as a disk in its own right, and check that it finds the
right&amp;nbsp;partitions:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;--read-only&lt;span class="w"&gt; &lt;/span&gt;--find&lt;span class="w"&gt; &lt;/span&gt;--show&lt;span class="w"&gt; &lt;/span&gt;--partscan&lt;span class="w"&gt; &lt;/span&gt;ubuntu-21.10-preinstalled-server-arm64+raspi.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;/dev/loop34
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;/dev/loop34*&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;brw-rw---- 1 root disk   7, 34 Oct 15 14:01 /dev/loop34
brw-rw---- 1 root disk 259,  6 Oct 15 14:01 /dev/loop34p1
brw-rw---- 1 root disk 259,  7 Oct 15 14:01 /dev/loop34p2&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;In the example above (your loop-device will likely have a different number),
the &amp;#8220;loop34p1&amp;#8221; and &amp;#8220;loop34p2&amp;#8221; block devices represent the first (boot)
partition of the image, and the second (root) partition. We can now use good
ol&amp;#8217; &lt;a class="reference external" href="https://manpages.ubuntu.com/manpages/jammy/en/man1/dd.1.html"&gt;disk-destroyer&lt;/a&gt; to perform the block transfers (as ever with &lt;tt class="docutils literal"&gt;dd&lt;/tt&gt;, be
&lt;em&gt;damned sure&lt;/em&gt; you get the &amp;#8220;of=&amp;#8221; device argument&amp;nbsp;correct):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/loop34p1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/mmcblk0p1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16M&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;16+0 records in
16+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 15.8294 s, 17.0 MB/s
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/loop34p2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/mapper/pivg-root&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16M&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;2030043136 bytes (2.0 GB, 1.9 GiB) copied, 1 s, 2.0 GB/s
226+1 records in
226+1 records out
3798978560 bytes (3.8 GB, 3.5 GiB) copied, 246.436 s, 15.4 MB/s&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;At this point, we can clean up the loop-device as we don&amp;#8217;t need anything else
from the source&amp;nbsp;image.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;/dev/loop34
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="root-cause"&gt;
&lt;h2&gt;Root&amp;nbsp;Cause&lt;/h2&gt;
&lt;p&gt;Now mount the new boot partition so we can do a bit of surgery on where it
should look for the root device (in our new logical&amp;nbsp;volume):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p1&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;bcm2710-rpi-2-b.dtb       bootcode.bin  fixup_x.dat     start_db.elf
bcm2710-rpi-3-b.dtb       boot.scr      initrd.img      start.elf
bcm2710-rpi-3-b-plus.dtb  cmdline.txt   meta-data       start_x.elf
bcm2710-rpi-cm3.dtb       config.txt    network-config  uboot_rpi_3.bin
bcm2711-rpi-400.dtb       fixup4cd.dat  overlays        uboot_rpi_4.bin
bcm2711-rpi-4-b.dtb       fixup4.dat    README          uboot_rpi_arm64.bin
bcm2711-rpi-cm4.dtb       fixup4db.dat  start4cd.elf    user-data
bcm2837-rpi-3-a-plus.dtb  fixup4x.dat   start4db.elf    vmlinuz
bcm2837-rpi-3-b.dtb       fixup_cd.dat  start4.elf
bcm2837-rpi-3-b-plus.dtb  fixup.dat     start4x.elf
bcm2837-rpi-cm3-io3.dtb   fixup_db.dat  start_cd.elf
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc quiet splash
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s,root=[^ ]*,root=/dev/mapper/pivg-root,'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mapper/pivg-root rootfstype=ext4 elevator=deadline rootwait fixrtc quiet splash&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Feel free to use an editor here instead of messing around with &lt;tt class="docutils literal"&gt;sed&lt;/tt&gt;;
I&amp;#8217;ve illustrated the changes necessary by showing the before and after
states of the file&amp;nbsp;affected.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Some (very similar) surgery is also required on the &lt;tt class="docutils literal"&gt;/etc/fstab&lt;/tt&gt; file on our
new root&amp;nbsp;volume:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/mapper/pivg-root&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;LABEL=writable  /        ext4   discard,errors=remount-ro       0 1
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s,^LABEL=writable,/dev/mapper/pivg-root,'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;/dev/mapper/pivg-root  /        ext4   discard,errors=remount-ro       0 1
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Finally, clean up all our mounts, deactivate the volume group containing our
root volume, and remove all the temporary mount-points we&amp;nbsp;created:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vgchange&lt;span class="w"&gt; &lt;/span&gt;-an&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rmdir&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rmdir&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot
&lt;/pre&gt;
&lt;p&gt;Now eject the &lt;span class="caps"&gt;SD&lt;/span&gt; card (or whatever storage you&amp;#8217;re using) and you should find it
boots happily on your Pi, with the root storage as &lt;span class="caps"&gt;LVM&lt;/span&gt;. Let me know below if
you run into any issues with&amp;nbsp;this!&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="ubuntu"></category><category term="storage"></category><category term="lvm"></category></entry><entry><title>Making Impish Impier</title><link href="https://waldorf.waveform.org.uk/2021/making-impish-impier.html" rel="alternate"></link><published>2021-10-15T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-10-15:/2021/making-impish-impier.html</id><summary type="html">&lt;p class="first last"&gt;Speeding up flash-kernel and update-initramfs on&amp;nbsp;Ubuntu&lt;/p&gt;
</summary><content type="html">&lt;p&gt;One of the things that constantly annoys me on Ubuntu (and therefore, one of
the things I&amp;#8217;m intending to fix this cycle) is how &lt;em&gt;bloody long&lt;/em&gt; it takes for
kernel upgrades to install! Most of this turns out to be down to two components
(which … ahem … my team is responsible for): &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;update-initramfs&lt;/span&gt;&lt;/tt&gt; which spends
nearly two minutes re-building the &lt;tt class="docutils literal"&gt;initrd.img&lt;/tt&gt; for the boot partition, and
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; which spends &lt;em&gt;another&lt;/em&gt; two minutes copying everything to the
boot&amp;nbsp;partition.&lt;/p&gt;
&lt;div class="section" id="under-com-pressure"&gt;
&lt;h2&gt;Under&amp;nbsp;(Com)pressure&lt;/h2&gt;
&lt;p&gt;At some point last week when I was testing a lot of different kernel bits (see
the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/when-hdmi-freezes-over.html"&gt;prior post&lt;/a&gt; for context!), this annoyed me to the point that I sat down
to dig into it. The first bit &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;update-initramfs&lt;/span&gt;&lt;/tt&gt; was a bit tricky to analyze:
there&amp;#8217;s no &lt;em&gt;really good&lt;/em&gt; means of profiling shell scripts sadly, so I wound up
using the old &lt;span class="caps"&gt;PS4&lt;/span&gt;-calling-date trick and writing a quick Python script to
analyze the results. Once this was done, however, one line stood out like a
sore thumb; the compression of the resulting&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;Impish has switched to using zstd as its compression algorithm and, while this
certainly compresses things better than gzip, it takes &lt;em&gt;ages&lt;/em&gt; to run on an &lt;span class="caps"&gt;ARM&lt;/span&gt;
processor. Specifically, even on an overclocked Pi 400 running at 2GHz it was
taking 85 seconds (!) to compress the image. A quick change to
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/initramfs-tools/initramfs.conf&lt;/span&gt;&lt;/tt&gt; and we can chop that down to 10 seconds
with&amp;nbsp;lz4:&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
&lt;span class="ln"&gt;50 &lt;/span&gt;&lt;span class="c1"&gt;#
&lt;/span&gt;&lt;span class="ln"&gt;51 &lt;/span&gt;&lt;span class="c1"&gt;# COMPRESS: [ gzip | bzip2 | lz4 | lzma | lzop | xz | zstd ]
&lt;/span&gt;&lt;span class="ln"&gt;52 &lt;/span&gt;&lt;span class="c1"&gt;#
&lt;/span&gt;&lt;span class="ln"&gt;53 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;54 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#COMPRESS=zstd
&lt;/span&gt;&lt;span class="ln"&gt;55 &lt;/span&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="nv"&gt;COMPRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;lz4
&lt;/pre&gt;
&lt;p&gt;Admittedly the resulting image is &lt;em&gt;much&lt;/em&gt; larger (~&lt;span class="caps"&gt;35MB&lt;/span&gt; with lz4 vs ~&lt;span class="caps"&gt;22MB&lt;/span&gt; with
zstd) and that will affect boot speed. However, even at &lt;span class="caps"&gt;SD&lt;/span&gt; card read speeds
that&amp;#8217;s only one second lost per boot vs 75 seconds per kernel install (and
lately I&amp;#8217;ve been doing about one kernel install per boot!). Anyway, consider
whether you do more or less than 75 boots per kernel install, and adjust&amp;nbsp;accordingly!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="flash-ahh-ahh"&gt;
&lt;h2&gt;Flash, ahh&amp;nbsp;ahh!&lt;/h2&gt;
&lt;p&gt;On Ubuntu (and for that matter, Debian), the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; tool is charged
with installing the bootloader, device-tree, kernel, and initrd (basically
everything needed to get the system started) to … wherever they need to be on a
given board. Sometimes that&amp;#8217;s some &lt;span class="caps"&gt;NVRAM&lt;/span&gt;, sometimes a special ext4 partition,
sometimes (as in the Pi&amp;#8217;s case) that&amp;#8217;s a &lt;span class="caps"&gt;FAT&lt;/span&gt; partition on some storage&amp;nbsp;medium.&lt;/p&gt;
&lt;p&gt;The Pi is quite an unusual case here inasmuch as its root storage is fully
expected to go walkies (such as when upgrading an &lt;span class="caps"&gt;SD&lt;/span&gt; card on an old Pi and
moving it to a new Pi model). Due to this, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; (in Ubuntu) copies
the device-tree files for &lt;em&gt;all&lt;/em&gt; Pi models (not just the one it finds itself on)
to the boot partition every time the kernel is updated (along with everything
else it usually copies). Unfortunately, the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; function that
handles each transfer is quite slow and has a lot of&amp;nbsp;overhead.&lt;/p&gt;
&lt;p&gt;For most boards, this doesn&amp;#8217;t matter as they copy (maybe) five files: a
bootloader, a kernel, an initrd, a device-tree, and possibly some firmware or
other scripts, or more often a single file which is some amalgam of the
aforementioned pieces. But for the Pi there&amp;#8217;s well over 200 individual files to
copy and the result is a major&amp;nbsp;slow-down.&lt;/p&gt;
&lt;p&gt;Once I dug into the code the cause was pretty obvious. In the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/share/flash-kernel/functions&lt;/span&gt;&lt;/tt&gt; script, the &lt;tt class="docutils literal"&gt;backup_and_install&lt;/tt&gt;
function on line 642 is the function we&amp;#8217;re interested&amp;nbsp;in:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-642"&gt;&lt;span class="linenos"&gt;642&lt;/span&gt;backup_and_install&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-643"&gt;&lt;span class="linenos"&gt;643&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-644"&gt;&lt;span class="linenos"&gt;644&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-645"&gt;&lt;span class="linenos"&gt;645&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;do_dot_bak&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;get_dot_bak_preference&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-646"&gt;&lt;span class="linenos"&gt;646&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mtd_backup_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;get_mtd_backup_dir&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-647"&gt;&lt;span class="linenos"&gt;647&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-648"&gt;&lt;span class="linenos"&gt;648&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$do_dot_bak&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-649"&gt;&lt;span class="linenos"&gt;649&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Taking backup of &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-650"&gt;&lt;span class="linenos"&gt;650&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;.bak&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-651"&gt;&lt;span class="linenos"&gt;651&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-652"&gt;&lt;span class="linenos"&gt;652&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Skipping backup of &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-653"&gt;&lt;span class="linenos"&gt;653&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-654"&gt;&lt;span class="linenos"&gt;654&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-655"&gt;&lt;span class="linenos"&gt;655&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# If we are installing to a filesystem which is not normally mounted&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-656"&gt;&lt;span class="linenos"&gt;656&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# then take a second copy in /var/backups, where they can e.g. be&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-657"&gt;&lt;span class="linenos"&gt;657&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# backed up.&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-658"&gt;&lt;span class="linenos"&gt;658&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$boot_mnt_dir&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$mtd_backup_dir&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-659"&gt;&lt;span class="linenos"&gt;659&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bak&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$mtd_backup_dir&lt;/span&gt;&lt;span class="s2"&gt;/&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-660"&gt;&lt;span class="linenos"&gt;660&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;#echo &amp;quot;Saving $boot_device:&amp;quot;$(basename &amp;quot;$source&amp;quot;)&amp;quot; in $bak&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-661"&gt;&lt;span class="linenos"&gt;661&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$mtd_backup_dir&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-662"&gt;&lt;span class="linenos"&gt;662&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$source&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$bak&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-663"&gt;&lt;span class="linenos"&gt;663&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-664"&gt;&lt;span class="linenos"&gt;664&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Installing new &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-665"&gt;&lt;span class="linenos"&gt;665&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$source&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-666"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;666&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;maybe_defrag&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-667"&gt;&lt;span class="linenos"&gt;667&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Yes, it&amp;#8217;s convoluted, but it needs to be for certain of the copying cases, and
&lt;em&gt;mostly&lt;/em&gt; it&amp;#8217;s pretty quick. However, it&amp;#8217;s the last line that&amp;#8217;s interesting.
That leads us to the &amp;#8220;maybe_defrag&amp;#8221; function on line&amp;nbsp;471:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-471"&gt;&lt;span class="linenos"&gt;471&lt;/span&gt;maybe_defrag&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-472"&gt;&lt;span class="linenos"&gt;472&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-473"&gt;&lt;span class="linenos"&gt;473&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bootloader-Has-Broken-Ext4-Extent-Support&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-474"&gt;&lt;span class="linenos"&gt;474&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;broken_fw
&lt;/span&gt;&lt;span id="line-475"&gt;&lt;span class="linenos"&gt;475&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-476"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;476&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;broken_fw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;get_machine_field&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$machine&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$field&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-477"&gt;&lt;span class="linenos"&gt;477&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-478"&gt;&lt;span class="linenos"&gt;478&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-479"&gt;&lt;span class="linenos"&gt;479&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$broken_fw&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-480"&gt;&lt;span class="linenos"&gt;480&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-481"&gt;&lt;span class="linenos"&gt;481&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-482"&gt;&lt;span class="linenos"&gt;482&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;df&lt;span class="w"&gt; &lt;/span&gt;--output&lt;span class="o"&gt;=&lt;/span&gt;fstype&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;1d&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ext4&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-483"&gt;&lt;span class="linenos"&gt;483&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-484"&gt;&lt;span class="linenos"&gt;484&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-485"&gt;&lt;span class="linenos"&gt;485&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;e4defrag&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-486"&gt;&lt;span class="linenos"&gt;486&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;error&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;e4defrag command not found, unable to defrag &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-487"&gt;&lt;span class="linenos"&gt;487&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-488"&gt;&lt;span class="linenos"&gt;488&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="w"&gt; &lt;/span&gt;e4defrag&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/null&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-489"&gt;&lt;span class="linenos"&gt;489&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;error&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;e4defrag of &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt; failed. Try freeing up space in /boot and re-executing flash-kernel&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-490"&gt;&lt;span class="linenos"&gt;490&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-491"&gt;&lt;span class="linenos"&gt;491&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;From working on &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;flash-kernel&lt;/span&gt;&lt;/tt&gt; in the past, I know that &amp;#8220;get_machine_field&amp;#8221;
calls are quite expensive (it&amp;#8217;s essentially using shell-script to parse a
text-file database). That&amp;#8217;s why there&amp;#8217;s a ton of calls caching these lookups
around line 1008. Ultimately the &amp;#8220;proper&amp;#8221; fix is to cache the result of that
call too. However, on the Pi the &amp;#8220;maybe_defrag&amp;#8221; function never does anything
(and will never do anything) anyway, so a quick hack is to simply comment out
that final line in&amp;nbsp;backup_and_install:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-642"&gt;&lt;span class="linenos"&gt;642&lt;/span&gt;backup_and_install&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-643"&gt;&lt;span class="linenos"&gt;643&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-644"&gt;&lt;span class="linenos"&gt;644&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-645"&gt;&lt;span class="linenos"&gt;645&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;do_dot_bak&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;get_dot_bak_preference&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-646"&gt;&lt;span class="linenos"&gt;646&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mtd_backup_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;get_mtd_backup_dir&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-647"&gt;&lt;span class="linenos"&gt;647&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-648"&gt;&lt;span class="linenos"&gt;648&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$do_dot_bak&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-649"&gt;&lt;span class="linenos"&gt;649&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Taking backup of &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-650"&gt;&lt;span class="linenos"&gt;650&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;.bak&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-651"&gt;&lt;span class="linenos"&gt;651&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-652"&gt;&lt;span class="linenos"&gt;652&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Skipping backup of &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-653"&gt;&lt;span class="linenos"&gt;653&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-654"&gt;&lt;span class="linenos"&gt;654&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-655"&gt;&lt;span class="linenos"&gt;655&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# If we are installing to a filesystem which is not normally mounted&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-656"&gt;&lt;span class="linenos"&gt;656&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# then take a second copy in /var/backups, where they can e.g. be&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-657"&gt;&lt;span class="linenos"&gt;657&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# backed up.&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-658"&gt;&lt;span class="linenos"&gt;658&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$boot_mnt_dir&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$mtd_backup_dir&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-659"&gt;&lt;span class="linenos"&gt;659&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bak&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$mtd_backup_dir&lt;/span&gt;&lt;span class="s2"&gt;/&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-660"&gt;&lt;span class="linenos"&gt;660&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;#echo &amp;quot;Saving $boot_device:&amp;quot;$(basename &amp;quot;$source&amp;quot;)&amp;quot; in $bak&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-661"&gt;&lt;span class="linenos"&gt;661&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$mtd_backup_dir&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-662"&gt;&lt;span class="linenos"&gt;662&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$source&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$bak&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-663"&gt;&lt;span class="linenos"&gt;663&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-664"&gt;&lt;span class="linenos"&gt;664&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Installing new &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-665"&gt;&lt;span class="linenos"&gt;665&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$source&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$dest&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-666"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;666&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;#maybe_defrag &amp;quot;$dest&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-667"&gt;&lt;span class="linenos"&gt;667&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once I&amp;#8217;d put these two changes in place, I found that kernel installs went from
taking more than 4 minutes, to slightly less than 2. Okay, still not fantastic
but I am operating on an &lt;span class="caps"&gt;SD&lt;/span&gt; card, and a halving of the time for two trivial
changes is not&amp;nbsp;bad!&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll get the &amp;#8220;proper&amp;#8221; fixes in place during the next development&amp;nbsp;cycle.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="impish"></category></entry><entry><title>Common Sense (HAT)</title><link href="https://waldorf.waveform.org.uk/2021/common-sense-hat.html" rel="alternate"></link><published>2021-10-14T00:00:00+01:00</published><updated>2022-10-07T22:47:22+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-10-14:/2021/common-sense-hat.html</id><summary type="html">&lt;p class="first last"&gt;A quick run-down of the new Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; support in&amp;nbsp;Ubuntu&lt;/p&gt;
</summary><content type="html">&lt;p&gt;One of the main new features in Ubuntu for the Raspberry Pi is the addition of
the packages to support the Raspberry Pi &lt;a class="reference external" href="https://www.raspberrypi.com/products/sense-hat/"&gt;Sense &lt;span class="caps"&gt;HAT&lt;/span&gt;&lt;/a&gt;. Specifically, the
packages you&amp;#8217;re most likely to be interested in&amp;nbsp;are:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;sense-hat&lt;/dt&gt;
&lt;dd&gt;The main Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; package which depends on all the other bits you&amp;#8217;ll need
(like the Python library detailed below) and a basic configuration with
some reasonable calibration&amp;nbsp;figures&lt;/dd&gt;
&lt;dt&gt;python3-sense-hat&lt;/dt&gt;
&lt;dd&gt;The official Python library for interfacing to the Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; (note: there&amp;#8217;s
no corresponding &lt;strong&gt;python-sense-hat&lt;/strong&gt; library for Python 2.x on Ubuntu
because Python 2.x is no longer&amp;nbsp;supported)&lt;/dd&gt;
&lt;dt&gt;sense-emu-tools&lt;/dt&gt;
&lt;dd&gt;The Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; desktop emulator package which includes the &lt;span class="caps"&gt;GUI&lt;/span&gt; desktop
emulator, and the &lt;strong&gt;sense_rec&lt;/strong&gt; and &lt;strong&gt;sense_play&lt;/strong&gt;&amp;nbsp;tools&lt;/dd&gt;
&lt;/dl&gt;
&lt;div class="section" id="fake-hats"&gt;
&lt;h2&gt;Fake&amp;nbsp;HATs&lt;/h2&gt;
&lt;p&gt;We&amp;#8217;ll start with the Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; desktop emulator package for a couple of reasons
(full disclosure: these reasons have nothing whatsoever to do with my having
written it … no, sir!). Firstly you can play with it even if you don&amp;#8217;t have a
Sense &lt;span class="caps"&gt;HAT&lt;/span&gt;, but secondly if you &lt;em&gt;do&lt;/em&gt; have a Sense &lt;span class="caps"&gt;HAT&lt;/span&gt;, then you can use all the
demo scripts that come with&amp;nbsp;it!&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s get started with installing the &lt;span class="caps"&gt;HAT&lt;/span&gt;. This is my (at this point positively
ancient) &lt;a class="reference external" href="https://www.raspberrypi.com/products/sense-hat/"&gt;Sense &lt;span class="caps"&gt;HAT&lt;/span&gt;&lt;/a&gt;, mounted on a Pimoroni &lt;a class="reference external" href="https://shop.pimoroni.com/products/mini-black-hat-hack3r?variant=19448026055"&gt;Black &lt;span class="caps"&gt;HAT&lt;/span&gt; Hacker&lt;/a&gt; board (because
it makes it far easier to wiggle around without worrying about &lt;span class="caps"&gt;HDMI&lt;/span&gt; cables and&amp;nbsp;such):&lt;/p&gt;
&lt;img alt="A Sense HAT installed on a Black HAT Hacker board, attached by a black ribbon cable to the GPIO header of a Pi 4B 8GB" src="https://waldorf.waveform.org.uk/images/sense-hat-install.jpg" /&gt;
&lt;p&gt;When you switch on the Pi you should see a rainbow image on the Sense &lt;span class="caps"&gt;HAT&lt;/span&gt;
(after an initially blinding flash, if you happen to be on a Pi 4 &amp;#8212; don&amp;#8217;t
worry, this is normal!). Once the kernel starts, the rainbow on the Sense &lt;span class="caps"&gt;HAT&lt;/span&gt;
will go&amp;nbsp;blank.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;If you&amp;#8217;re using the Ubuntu Desktop image, you may see the LEDs on the Sense
&lt;span class="caps"&gt;HAT&lt;/span&gt; show an orange strip on a white background which I &lt;em&gt;think&lt;/em&gt; is plymouth
trying to show an Ubuntu logo on a &lt;em&gt;very&lt;/em&gt; low-res screen (you don&amp;#8217;t see
this on the Server image, which has no &amp;#8220;pretty&amp;#8221; boot&amp;nbsp;screen).&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Once you&amp;#8217;re logged in and at a terminal prompt, install the packages we&amp;nbsp;want:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo apt install sense-emu-tools sense-hat
&lt;/pre&gt;
&lt;p&gt;Next, install a decent code editor. Below I&amp;#8217;m using Geany (anyone who knows me
is now crying &amp;#8220;Wot! No vim?&amp;#8221;, but that&amp;#8217;s not something I&amp;#8217;d foist upon the&amp;nbsp;unsuspecting):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo apt install geany
&lt;/pre&gt;
&lt;p&gt;Next, start the desktop emulator, which you should find under Gnome&amp;#8217;s
application menu (or you can hit the Super key and just type &amp;#8220;Sense&amp;#8221;; it should
find&amp;nbsp;it):&lt;/p&gt;
&lt;img alt="Selecting the Sense HAT Emulator icon from the Gnome applications menu" src="https://waldorf.waveform.org.uk/images/sense-emu-start.png" /&gt;
&lt;img alt="A screenshot of the Sense HAT Emulator's main window just after opening it." src="https://waldorf.waveform.org.uk/images/sense-emu-open.png" /&gt;
&lt;p&gt;Finally (because I&amp;#8217;m silly and forgot this bit), configure it to use &lt;tt class="docutils literal"&gt;geany&lt;/tt&gt;
(or &lt;em&gt;your-choice-of-editor-here&lt;/em&gt;) because the default, &lt;tt class="docutils literal"&gt;thonny&lt;/tt&gt;, isn&amp;#8217;t in the
Ubuntu archive …&amp;nbsp;yet:&lt;/p&gt;
&lt;img alt="The Sense HAT Emulator preferences window, displaying the editor set to &amp;quot;geany&amp;quot;, and with both &amp;quot;Simulate&amp;quot; options ticked for full sensor emulation" src="https://waldorf.waveform.org.uk/images/sense-emu-prefs.png" /&gt;
&lt;p&gt;While you&amp;#8217;re at it, I&amp;#8217;d suggest activating the &amp;#8220;Simulate environment sensors&amp;#8221;
and &amp;#8220;Simulate inertial measurement unit&amp;#8221; options too, as these make the
emulation of these sensors more &amp;#8220;realistic&amp;#8221; (on those platforms with enough
computing power, which is pretty much any modern computer with the exception of
the Pi&amp;nbsp;Zero).&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;The Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; emulator runs on any machine, not just the Raspberry Pi (you
can install it on your Ubuntu &lt;span class="caps"&gt;PC&lt;/span&gt; running Impish), but we&amp;#8217;re using on a Pi
here partly to show how to use the emulator&amp;#8217;s demo scripts with the real
Sense &lt;span class="caps"&gt;HAT&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s open up the &lt;tt class="docutils literal"&gt;humidity.py&lt;/tt&gt; demo script. Go to &amp;#8220;File / Open example /
Simple / humidity.py&amp;#8221; in the menus. This should fire up your chosen editor with
a copy of this script, written to your home directory with a timestamp
appended so that you can edit it as much as you&amp;#8217;d like and subsequent
selections of that entry will generate a &amp;#8220;pristine&amp;#8221; copy of the&amp;nbsp;original:&lt;/p&gt;
&lt;img alt="Selecting the humidity.py example script from the Sense HAT &amp;quot;File, Open example&amp;quot; menu." src="https://waldorf.waveform.org.uk/images/sense-emu-humidity-open.png" /&gt;
&lt;img alt="The humidity.py example script open in the Geany programmers editor." src="https://waldorf.waveform.org.uk/images/geany-humidity-emu.png" /&gt;
&lt;p&gt;If you&amp;#8217;re using Geany (as suggested), there&amp;#8217;s one more thing we need to before
we can directly run this script. Geany still defaults to trying to call
&lt;tt class="docutils literal"&gt;python&lt;/tt&gt; rather than &lt;tt class="docutils literal"&gt;python3&lt;/tt&gt; when running Python scripts. Since Ubuntu no
longer ships Python 2.x, the only interpreter is &lt;tt class="docutils literal"&gt;python3&lt;/tt&gt; (unless you&amp;#8217;ve
installed the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python-is-python3&lt;/span&gt;&lt;/tt&gt; package), so we need to tell it to use this
instead. Select &amp;#8220;Build / Set Build Commands&amp;#8221; from the Geany menu and change the
Execute command to&amp;nbsp;python3:&lt;/p&gt;
&lt;img alt="The Geany &amp;quot;Set Build Commands&amp;quot; dialog, showing the Compile and Execute commands with &amp;quot;python3&amp;quot; instead of &amp;quot;python&amp;quot;." src="https://waldorf.waveform.org.uk/images/geany-build-commands.png" /&gt;
&lt;p&gt;Finally, we can run our demo script. Select &amp;#8220;Build / Execute&amp;#8221; from the menu, or
click the cogs icon in the toolbar. You should see the display on the emulated
Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; turn partly white and partly green. The number of green elements
indicates the current humidity. If you drag the humidity slider up and down you
should see the number of green elements change&amp;nbsp;accordingly.&lt;/p&gt;
&lt;img alt="A full desktop screenshot showing the Geany editor with the script, the Sense HAT emulator window, and a blank terminal window (which contains the Python 3 interpreter running the script)." src="https://waldorf.waveform.org.uk/images/sense-emu-humidity-running.png" /&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;If you activated environment simulation earlier, there may be some delay
between you dragging the slider and the display updating. This is because
the real sensor typically displays a certain delay in responding to
changing humidity which the simulation&amp;nbsp;emulates.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;When you want to stop the script, press Ctrl+C in the (apparently empty)
terminal window that appeared when you ran the script (that terminal actually
contains the Python interpreter running your script). Feel free to modify the
script and see what difference it makes when you run it. Simple changes could
be modifying the colours used, more complex ones could change the orientation
or appearance of the&amp;nbsp;&amp;#8220;graph&amp;#8221;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="real-hats"&gt;
&lt;h2&gt;Real&amp;nbsp;HATs&lt;/h2&gt;
&lt;p&gt;What about running our humidity script on the &lt;em&gt;real&lt;/em&gt; Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; though? This is
pretty easy too. Firstly, edit the script to import from &lt;tt class="docutils literal"&gt;sense_hat&lt;/tt&gt; instead
of &lt;tt class="docutils literal"&gt;sense_emu&lt;/tt&gt;. This is the only code change that&amp;#8217;s&amp;nbsp;necessary:&lt;/p&gt;
&lt;img alt="The Geany editor showing the modified script, with the &amp;quot;sense_emu&amp;quot; import replaced with &amp;quot;sense_hat&amp;quot;." src="https://waldorf.waveform.org.uk/images/geany-humidity-hat.png" /&gt;
&lt;p&gt;Next, we need to ensure our script starts as &amp;#8220;root&amp;#8221;. This is because the I²C
bus that the Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; uses is only accessible to root (by default, see below)
under Ubuntu currently (this is something I need to correct &amp;#8212; but carefully &amp;#8212;
before the next release!). Again, bring up Geany&amp;#8217;s &amp;#8220;Build / Set Build Commands&amp;#8221;
dialog, and change the Execute command to include a &amp;#8220;sudo&amp;#8221; prefix (i.e. it
should read &lt;tt class="docutils literal"&gt;sudo python3 &amp;quot;%f&amp;quot;&lt;/tt&gt;:&lt;/p&gt;
&lt;img alt="The Geany Set Build Commands dialog showing a &amp;quot;sudo&amp;quot; prefix on the Execute Command." src="https://waldorf.waveform.org.uk/images/geany-build-commands-sudo.png" /&gt;
&lt;p&gt;Once again, select &amp;#8220;Build / Execute&amp;#8221; from the menu. This time, the terminal
that appears will prompt for your password (which &lt;a class="reference external" href="http://manpages.ubuntu.com/manpages/impish/en/man8/sudo.8.html"&gt;sudo&lt;/a&gt; needs to run things
as root). Once entered you should see the script running on the actual Sense
&lt;span class="caps"&gt;HAT&lt;/span&gt; on your&amp;nbsp;Pi:&lt;/p&gt;
&lt;img alt="An actual Sense HAT running the humidity script seen earlier." src="https://waldorf.waveform.org.uk/images/sense-hat-humidity.jpg" /&gt;
&lt;p&gt;In the image above the reading is quite high as I&amp;#8217;ve just breathed on the &lt;span class="caps"&gt;HAT&lt;/span&gt;.
You can try the same and watch the reading spike up before falling back down
(assuming your ambient relative humidity is fairly low that&amp;nbsp;is!)&lt;/p&gt;
&lt;p&gt;At this point, I&amp;#8217;d suggest having a play with the other demo scripts available
in the emulator. There&amp;#8217;s plenty in there that deal with the various
environmental sensors, the display, the joystick, and the &lt;span class="caps"&gt;IMU&lt;/span&gt; on the &lt;span class="caps"&gt;HAT&lt;/span&gt;. Have&amp;nbsp;fun!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="rootless"&gt;
&lt;h2&gt;Rootless&lt;/h2&gt;
&lt;p&gt;If you want to use the Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; without resorting to sudo or root, you need to
add some udev rules to permit access to regular users. Add the following
content to a file named &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/udev/rules.d/99-user-sense.rules&lt;/span&gt;&lt;/tt&gt; (you will
need to be root to create/edit this&amp;nbsp;file):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
SUBSYSTEM==&amp;quot;i2c-dev&amp;quot;, KERNEL==&amp;quot;i2c-[0123456]&amp;quot;, GROUP=&amp;quot;plugdev&amp;quot;, MODE=&amp;quot;0660&amp;quot;
SUBSYSTEM==&amp;quot;input&amp;quot;, ENV{LIBINPUT_DEVICE_GROUP}==&amp;quot;*:rpi-sense-joy&amp;quot;, GROUP=&amp;quot;plugdev&amp;quot;, MODE=&amp;quot;0660&amp;quot;
SUBSYSTEM==&amp;quot;graphics&amp;quot;, ENV{ID_PATH}==&amp;quot;*-rpi-sense-fb&amp;quot;, GROUP=&amp;quot;plugdev&amp;quot;, MODE=&amp;quot;0660&amp;quot;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;If you&amp;#8217;re typing this instead of copying and pasting, pay close attention
to the subtle distinctions between &amp;#8220;==&amp;#8221; and &amp;#8220;=&amp;#8221;. The first entries on a
line use &amp;#8220;==&amp;#8221; as they&amp;#8217;re &lt;em&gt;matching&lt;/em&gt; events; the latter entries use &amp;#8220;=&amp;#8221; as
they&amp;#8217;re &lt;em&gt;assigning&lt;/em&gt; values to the&amp;nbsp;device.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;After creating/editing this file, reboot the machine and you should find that
you can access your Sense &lt;span class="caps"&gt;HAT&lt;/span&gt; without&amp;nbsp;&amp;#8220;sudo&amp;#8221;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="sense-hat"></category><category term="hat"></category><category term="impish"></category></entry><entry><title>When HDMI freezes over</title><link href="https://waldorf.waveform.org.uk/2021/when-hdmi-freezes-over.html" rel="alternate"></link><published>2021-10-13T00:00:00+01:00</published><updated>2022-07-22T10:50:00+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-10-13:/2021/when-hdmi-freezes-over.html</id><summary type="html">&lt;p class="first last"&gt;On the teething pains of the Impish desktop release, and the joys of
owning old&amp;nbsp;monitors&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Well, it&amp;#8217;s time for another Ubuntu release, and so time for another slew of
blog posts from me! In this particular case though, I need to start with a mea
culpa on the state of the Ubuntu Desktop for Raspberry Pi&amp;nbsp;release.&lt;/p&gt;
&lt;div class="section" id="the-bug-the-bodge-and-the-deadline"&gt;
&lt;h2&gt;The bug, the bodge, and the&amp;nbsp;deadline&lt;/h2&gt;
&lt;p&gt;Shortly before release, a bug in &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/linux-raspi/+bug/1946368"&gt;&lt;span class="caps"&gt;HDMI&lt;/span&gt; output handling&lt;/a&gt; (initially part of
&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/linux-raspi/+bug/1944397"&gt;another bug&lt;/a&gt;) was discovered which vexed us right up to the release itself.
The symptoms ranged from the annoying (display output freezes after a certain
amount of use, usually a couple of hours in my case) to the critical (display
output freezes during boot, in the case of a&amp;nbsp;colleague).&lt;/p&gt;
&lt;p&gt;The determining factor eventually turned out to be something to do with high
refresh-rate monitors. With my eye-sight, I&amp;#8217;ve never wanted or needed anything
beyond good ol&amp;#8217; 1080p at 60Hz … and so that&amp;#8217;s what all my monitors are. But
&lt;a class="reference external" href="https://blogjawn.stufftoread.com/"&gt;younger colleagues&lt;/a&gt; (with better eye-sight) have fancier displays with higher
frame-rates and on these the issue was most definitely&amp;nbsp;critical.&lt;/p&gt;
&lt;p&gt;We quickly found a work-around (which is in the &lt;a class="reference external" href="https://discourse.ubuntu.com/t/impish-indri-release-notes/21951"&gt;release notes&lt;/a&gt;) which was
simply to replace the &amp;#8220;kms&amp;#8221; overlay in &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; with the &amp;#8220;fkms&amp;#8221; overlay,
but then the race was on to fix things before&amp;nbsp;release.&lt;/p&gt;
&lt;p&gt;The kernel team worked tirelessly to try and come up with a fix, but it quickly
became apparent this was an upstream issue too, and that it was unlikely we&amp;#8217;d
find a fix on our own. Eventually it was obvious that the bug was going to make
it into the release images, and so the debate switched to whether to release
with the fkms workaround in&amp;nbsp;place.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="to-bodge-or-not-to-bodge"&gt;
&lt;h2&gt;To bodge, or not to&amp;nbsp;bodge&lt;/h2&gt;
&lt;p&gt;As will be obvious to anyone who&amp;#8217;s tried the desktop release with a
high-refresh rate monitor, we released &lt;em&gt;without&lt;/em&gt; the workaround in place (and
that&amp;#8217;s the royal we, because ultimately this was &lt;em&gt;my&lt;/em&gt; decision so if you&amp;#8217;re
going to yell at anyone, yell at &lt;em&gt;me&lt;/em&gt; &amp;#8212; comments&amp;nbsp;below!).&lt;/p&gt;
&lt;p&gt;By way of (lengthy) explanation, here was my&amp;nbsp;reasoning:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;We have regular meetings with the Pi Foundation (or more precisely the
engineers at Raspberry Pi Trading Ltd. — just to appease Ben&amp;#8217;s nagging of my
constant conflation of these entities!). My understanding from these
meetings is the &amp;#8220;kms&amp;#8221; overlay is considered the &amp;#8220;future direction of
development&amp;#8221;. As such, it is under active development in the upstream&amp;nbsp;kernel.&lt;/li&gt;
&lt;li&gt;The &amp;#8220;fkms&amp;#8221; overlay is considered legacy, and while it is supported in the
kernel in use in Raspberry Pi &lt;span class="caps"&gt;OS&lt;/span&gt;, if it happens to break in the development
kernel (which is where the Ubuntu kernel patches are sourced from), that&amp;#8217;s&amp;nbsp;acceptable.&lt;/li&gt;
&lt;li&gt;We have a mechanism for modifying the boot configuration during a release
upgrade, but no such mechanism currently exists for kernel upgrades. The
modifications performed during release upgrades are &lt;em&gt;not&lt;/em&gt; perfect (there are
edge cases we cannot reasonably account for), but a release upgrade is a
sufficiently major operation that a certain amount of breakage might be
considered reasonable in the case of esoteric configurations. The same
cannot be said of kernel&amp;nbsp;upgrades.&lt;/li&gt;
&lt;li&gt;Other (minor) points against &amp;#8220;fkms&amp;#8221;: audio doesn&amp;#8217;t operate correctly
(choppy) under &amp;#8220;fkms&amp;#8221; without the &amp;#8220;tsched=0&amp;#8221; workaround, and video displays
screen-tearing. These are obviously non-critical, but it&amp;#8217;s a sub-par
experience compared to fixing &amp;#8220;kms&amp;#8221;&amp;nbsp;properly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To summarise: we could switch people to the &amp;#8220;fkms&amp;#8221; overlay during the impish
release upgrade (and obviously modify the release image to use &amp;#8220;fkms&amp;#8221; too) but
that would leave us in a position where a future kernel upgrade during the
release broke things (in fact, our testing with a few interim upstream kernels
suggested this was already the case), and there was no good mechanism for
fiddling with the boot configuration during a regular &lt;tt class="docutils literal"&gt;apt update&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;apt
upgrade&lt;/tt&gt; procedure.&lt;/p&gt;
&lt;p&gt;Alternatively, we could release with the &amp;#8220;kms&amp;#8221; overlay knowing full well that
would break badly on certain high-fps monitors, but with a release noted
work-around that wasn&amp;#8217;t &lt;em&gt;terribly&lt;/em&gt; complex (editing one line in a text file,
which was accessible on all platforms), and the hope that we could fix the
kernel in a (quickly released) update. Obviously this would still entail pain
for people having to use the workaround to even boot (in order to install such
an upgrade). But it would also mean that such people would have experience of
installing the workaround and thus would have little difficulty in removing it
themselves, without having to bodge a postinst script into the kernel to try
removing it (probably&amp;nbsp;imperfectly).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion …&amp;nbsp;?&lt;/h2&gt;
&lt;p&gt;As it turns out, we may have a fix quicker than I&amp;#8217;d hoped: as I write this
final section, I&amp;#8217;m just waiting on another &lt;span class="caps"&gt;SD&lt;/span&gt; card to flash to play with a
testing kernel which may fix at least part of the issue (I wasn&amp;#8217;t kidding when
I said the kernel team were working tirelessly on this!). There are also a
couple of patches that &lt;em&gt;appear&lt;/em&gt; to stabilize things completely, at least on my
old 1080p 60Hz&amp;nbsp;monitor.&lt;/p&gt;
&lt;p&gt;Anyway, to all those affected, all I can say for now is &amp;#8220;sorry&amp;#8221; that this
release isn&amp;#8217;t everything I&amp;#8217;d hoped it to be, and that the release includes
&lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/linux-raspi/+bug/1946368"&gt;this issue&lt;/a&gt;. That&amp;#8217;s on me: I could&amp;#8217;ve made the call to switch to &amp;#8220;fkms&amp;#8221; for
the release, but I genuinely think that although this is obviously the more
painful course, it&amp;#8217;s the right one for a better experience in the&amp;nbsp;end.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="desktop"></category><category term="impish"></category></entry><entry><title>You boot — no, u-boot first!</title><link href="https://waldorf.waveform.org.uk/2021/you-boot-no-u-boot-first.html" rel="alternate"></link><published>2021-05-05T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-05-05:/2021/you-boot-no-u-boot-first.html</id><summary type="html">&lt;p class="first last"&gt;Bypassing U-Boot to enable &lt;span class="caps"&gt;USB&lt;/span&gt; boot on Ubuntu&amp;nbsp;Focal&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A frequently asked question on the various #ubuntu-* channels I lurk around on
FreeNode is that of getting Ubuntu Focal (20.04) booting from &lt;span class="caps"&gt;USB&lt;/span&gt; on a Pi. The
major issue here is that Focal still uses U-Boot and that doesn&amp;#8217;t work with
&lt;span class="caps"&gt;USB&lt;/span&gt; boot on the Pi (our U-Boot build does now support both the &lt;span class="caps"&gt;USB2&lt;/span&gt; and &lt;span class="caps"&gt;USB3&lt;/span&gt;
ports on the Pi 4, but the &lt;span class="caps"&gt;MSD&lt;/span&gt; support doesn&amp;#8217;t work for some&amp;nbsp;reason).&lt;/p&gt;
&lt;p&gt;However, U-Boot isn&amp;#8217;t critical to booting Ubuntu. In fact, on the armhf images
it&amp;#8217;s basically redundant. On the arm64 images though it does have one very
important job: decompressing the gzip&amp;#8217;d&amp;nbsp;kernel.&lt;/p&gt;
&lt;p&gt;How come the kernel doesn&amp;#8217;t decompress itself? Because arm64 kernels can&amp;#8217;t. For
whatever reason, the Linux kernel includes self-extraction code for x86 and
armhf … but not arm64 where &amp;#8220;that&amp;#8217;s the bootloader&amp;#8217;s job&amp;#8221;. Around the time of
Focal&amp;#8217;s release, the Pi&amp;#8217;s native bootloader didn&amp;#8217;t support any kind of
extraction, so we shipped a U-Boot script to handle decompressing the gzip&amp;#8217;d&amp;nbsp;kernel.&lt;/p&gt;
&lt;p&gt;Later on in 2020, the Pi&amp;#8217;s bootloader got an upgrade to support decompressing
gzip&amp;#8217;d kernels but Focal&amp;#8217;s boot sequence still uses U-Boot because forcing a
boot sequence change on existing installations is not something I&amp;#8217;m keen to do
unless absolutely necessary (that said, it&amp;#8217;s something that&amp;#8217;ll need tackling
for the upgrade to the next &lt;span class="caps"&gt;LTS&lt;/span&gt;).&lt;/p&gt;
&lt;div class="section" id="on-a-fresh-install"&gt;
&lt;h2&gt;On a Fresh&amp;nbsp;Install&lt;/h2&gt;
&lt;p&gt;First let&amp;#8217;s tackle installing a completely new Focal image on a &lt;span class="caps"&gt;USB&lt;/span&gt; &lt;span class="caps"&gt;SSD&lt;/span&gt; and
adapting it so it&amp;#8217;ll boot from &lt;span class="caps"&gt;USB&lt;/span&gt;. Bear in mind your Pi 4 will already need to
be configured to &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/installing-the-new-pi-ubuntu-desktop.html#usb-hdd-ssd"&gt;boot from &lt;span class="caps"&gt;USB&lt;/span&gt;&lt;/a&gt; so do that first if you haven&amp;#8217;t already (this
is one-time setup though so if you can already boot your Pi 4 from &lt;span class="caps"&gt;USB&lt;/span&gt;, you&amp;#8217;re
all&amp;nbsp;set).&lt;/p&gt;
&lt;p&gt;Flash the Focal arm64 image (which must be point release 2 or later) straight
to your &lt;span class="caps"&gt;SSD&lt;/span&gt; drive. The easiest way to do this is with the &lt;a class="reference external" href="https://www.raspberrypi.org/software/"&gt;Raspberry Pi imaging
utility&lt;/a&gt;:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Slot your &lt;span class="caps"&gt;SSD&lt;/span&gt; drive into a &lt;span class="caps"&gt;USB3&lt;/span&gt; enclosure of some sort (personally I find
the &lt;a class="reference external" href="https://www.amazon.co.uk/gp/product/B00IJNDBM4"&gt;Inateck &lt;span class="caps"&gt;FE2004&lt;/span&gt;&lt;/a&gt; works nicely) and plug that into your &lt;span class="caps"&gt;PC&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Start up the imaging&amp;nbsp;utility&lt;/li&gt;
&lt;li&gt;Select &amp;#8220;&lt;span class="caps"&gt;CHOOSE&lt;/span&gt; &lt;span class="caps"&gt;OS&lt;/span&gt;&amp;#8221;, &amp;#8220;Other general purpose &lt;span class="caps"&gt;OS&lt;/span&gt;&amp;#8221;, &amp;#8220;Ubuntu&amp;#8221;, &amp;#8220;Ubuntu Server
20.04.2 &lt;span class="caps"&gt;LTS&lt;/span&gt; (RPi3/4/400)&amp;#8221; (if you&amp;#8217;re reading this from the future, any later
point release like .3 will be fine&amp;nbsp;too)&lt;/li&gt;
&lt;li&gt;Select &amp;#8220;&lt;span class="caps"&gt;CHOOSE&lt;/span&gt; &lt;span class="caps"&gt;STORAGE&lt;/span&gt;&amp;#8221; and pick your &lt;span class="caps"&gt;SSD&lt;/span&gt; drive (if you&amp;#8217;ve no other
removable storage attached it&amp;#8217;ll probably be the only thing&amp;nbsp;listed)&lt;/li&gt;
&lt;li&gt;Select &amp;#8220;&lt;span class="caps"&gt;WRITE&lt;/span&gt;&amp;#8221; and twiddle thumbs / make coffee while everything&amp;nbsp;finishes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, jump to the &amp;#8220;Boot Surgery&amp;#8221; section&amp;nbsp;below!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="from-an-existing-install"&gt;
&lt;h2&gt;From an Existing&amp;nbsp;Install&lt;/h2&gt;
&lt;p&gt;Converting an existing &lt;span class="caps"&gt;SD&lt;/span&gt; card installation is a very similar procedure. First
we&amp;#8217;ll deal with transferring the content of the &lt;span class="caps"&gt;SD&lt;/span&gt; card to the &lt;span class="caps"&gt;SSD&lt;/span&gt;. I&amp;#8217;m
assuming here that your &lt;span class="caps"&gt;SSD&lt;/span&gt; is at least as large as your &lt;span class="caps"&gt;SD&lt;/span&gt; card. If that isn&amp;#8217;t
the case you&amp;#8217;ll need to go and read about shrinking partitions and such-like
but it sounds rather unlikely so I&amp;#8217;m going to skip all that&amp;nbsp;here.&lt;/p&gt;
&lt;p&gt;The first step is simply to make sure that your installation is up to date. You
&lt;em&gt;must&lt;/em&gt; have an updated bootloader for this to work, so boot your &lt;span class="caps"&gt;SD&lt;/span&gt; card and
run a typical &lt;tt class="docutils literal"&gt;sudo apt update&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;sudo apt upgrade&lt;/tt&gt; cycle. Then shut the Pi
down, and get a machine running Linux other than your Pi with an &lt;span class="caps"&gt;SD&lt;/span&gt; card reader
and a &lt;span class="caps"&gt;USB&lt;/span&gt;&amp;nbsp;port.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;It is possible to do this on a Pi, especially one that&amp;#8217;s already &lt;span class="caps"&gt;USB&lt;/span&gt; booted
where you can use the internal &lt;span class="caps"&gt;SD&lt;/span&gt; reader as a source, and a spare &lt;span class="caps"&gt;USB&lt;/span&gt; port
as a target, but that&amp;#8217;s rather circular so for the purposes of this article
let&amp;#8217;s assume you&amp;#8217;ve got an Ubuntu &lt;span class="caps"&gt;PC&lt;/span&gt; lying&amp;nbsp;around!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;On your &lt;span class="caps"&gt;PC&lt;/span&gt;, insert your &lt;span class="caps"&gt;SD&lt;/span&gt; card and your &lt;span class="caps"&gt;SSD&lt;/span&gt; in some appropriate &lt;span class="caps"&gt;USB&lt;/span&gt; enclosure.
I&amp;#8217;m going to assume your &lt;span class="caps"&gt;SD&lt;/span&gt; card appears as &lt;tt class="docutils literal"&gt;/dev/mmcblk0&lt;/tt&gt; and your &lt;span class="caps"&gt;USB&lt;/span&gt;
enclosed &lt;span class="caps"&gt;SSD&lt;/span&gt; as &lt;tt class="docutils literal"&gt;/dev/sdf&lt;/tt&gt; below. If you want to be certain, &lt;em&gt;before&lt;/em&gt;
inserting the devices run &lt;code class="bash"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dmesg&lt;span class="w"&gt; &lt;/span&gt;-w&lt;/code&gt; and watch the output as you insert
them. The device names should be apparent from the&amp;nbsp;output.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;First, ensure nothing on the &lt;span class="caps"&gt;SD&lt;/span&gt; card or &lt;span class="caps"&gt;SSD&lt;/span&gt; is mounted: &lt;code class="bash"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt;
&lt;/span&gt;/dev/mmcblk0p1&lt;/code&gt;, &lt;code class="bash"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmblk0p2&lt;/code&gt;, …, &lt;code class="bash"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt;
&lt;/span&gt;/dev/sdf1&lt;/code&gt;, &lt;code class="bash"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/sdf2&lt;/code&gt;. Check the output of &lt;code class="bash"&gt;mount&lt;/code&gt;
to make sure no partitions from these devices are still&amp;nbsp;mounted.&lt;/li&gt;
&lt;li&gt;Next transfer everything from the source to the target with good ol&amp;#8217;
disk-destroyer: &lt;code class="bash"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/mmcblk0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16M&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;/code&gt;; be &lt;strong&gt;damned sure&lt;/strong&gt; you get the &lt;tt class="docutils literal"&gt;of=&lt;/tt&gt; device name
correct&amp;nbsp;here!&lt;/li&gt;
&lt;li&gt;Be warned this transfer will take &lt;em&gt;much&lt;/em&gt; longer than just writing a normal
image as it&amp;#8217;ll include all the blank unused blocks on the &lt;span class="caps"&gt;SD&lt;/span&gt; card too, so
prepare for much thumb twiddling / coffee&amp;nbsp;drinking!&lt;/li&gt;
&lt;li&gt;Run &lt;code class="bash"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;sync&lt;/code&gt; (probably unnecessary but no harm in a little&amp;nbsp;paranoia)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now continue with&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="boot-surgery"&gt;
&lt;h2&gt;Boot&amp;nbsp;Surgery&lt;/h2&gt;
&lt;p&gt;Now for some surgery on the boot configuration. Unplug the &lt;span class="caps"&gt;SSD&lt;/span&gt;&amp;#8217;s enclosure from
your &lt;span class="caps"&gt;PC&lt;/span&gt; and then plug it back in so that the freshly written boot partition
(labelled &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;system-boot&lt;/span&gt;&lt;/tt&gt;) auto-mounts (if your &lt;span class="caps"&gt;PC&lt;/span&gt; is running Linux, the
&lt;tt class="docutils literal"&gt;writable&lt;/tt&gt; partition will likely auto-mount too, but &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;system-boot&lt;/span&gt;&lt;/tt&gt; should
auto-mount on everything including Windows and Mac &lt;span class="caps"&gt;OS&lt;/span&gt;&amp;nbsp;X).&lt;/p&gt;
&lt;p&gt;On the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;system-boot&lt;/span&gt;&lt;/tt&gt; partition that&amp;#8217;s mounted, find the &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; file
and open it in a text editor (I &lt;em&gt;think&lt;/em&gt; Notepad can handle &lt;span class="caps"&gt;UNIX&lt;/span&gt; line-endings
these days; Windows users please let me know if this isn&amp;#8217;t the case!). It
should look like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="c1"&gt;# Please DO NOT modify this file; if you need to modify the boot config, the&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# &amp;quot;usercfg.txt&amp;quot; file is the place to include user changes. Please refer to&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# the README file for a description of the various configuration files on&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# the boot partition.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# The unusual ordering below is deliberate; older firmwares (in particular the&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# version initially shipped with bionic) don't understand the conditional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# [sections] below and simply ignore them. The Pi4 doesn't boot at all with&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# firmwares this old so it's safe to place at the top. Of the Pi2 and Pi3, the&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Pi3 uboot happens to work happily on the Pi2, so it needs to go at the bottom&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# to support old firmwares.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_4.bin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;[pi2]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_2.bin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;[pi3]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_3.bin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;device_tree_address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0x03000000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# The following settings are &amp;quot;defaults&amp;quot; expected to be overridden by the&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# included configuration. The only reason they are included is, again, to&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;29 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;# support old firmwares which don't understand the &amp;quot;include&amp;quot; command.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;31 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;enable_uart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;33 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;include syscfg.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;35 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;include usercfg.txt&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;First off, ignore that warning at the top (I wrote it, so take it from me that
you can ignore it!). I&amp;#8217;ve covered why it&amp;#8217;s there and &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/boot-configuration-with-pibootctl.html"&gt;why it was wrong&lt;/a&gt;
before, so go read that if you&amp;#8217;re interested, but for now all that&amp;#8217;s important
is that you can happily modify this file without breaking stuff (if you&amp;#8217;re
reasonably careful). In fact, feel free to delete that comment&amp;nbsp;:-)&lt;/p&gt;
&lt;p&gt;Now, comment out all the &lt;tt class="docutils literal"&gt;kernel=&lt;/tt&gt; lines pointing to U-boot, and the
&lt;tt class="docutils literal"&gt;device_tree_address=&lt;/tt&gt; line. Then under the &lt;code class="ini"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;&lt;/code&gt; section add
&lt;tt class="docutils literal"&gt;kernel=vmlinuz&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;initramfs initrd.img followkernel&lt;/tt&gt;. Your file should
wind up looking something like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span id="line-1"&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c1"&gt;# The unusual ordering below is deliberate; older firmwares (in particular the&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-2"&gt;&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="c1"&gt;# version initially shipped with bionic) don&amp;#39;t understand the conditional&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-3"&gt;&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="c1"&gt;# [sections] below and simply ignore them. The Pi4 doesn&amp;#39;t boot at all with&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-4"&gt;&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="c1"&gt;# firmwares this old so it&amp;#39;s safe to place at the top. Of the Pi2 and Pi3, the&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-5"&gt;&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="c1"&gt;# Pi3 uboot happens to work happily on the Pi2, so it needs to go at the bottom&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-6"&gt;&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="c1"&gt;# to support old firmwares.&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-7"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-8"&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-9"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="c1"&gt;#kernel=uboot_rpi_4.bin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-10"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-11"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-12"&gt;&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="k"&gt;[pi2]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-13"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c1"&gt;#kernel=uboot_rpi_2.bin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-14"&gt;&lt;span class="linenos"&gt;14&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-15"&gt;&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="k"&gt;[pi3]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-16"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="c1"&gt;#kernel=uboot_rpi_3.bin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-17"&gt;&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-18"&gt;&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-19"&gt;&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-20"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;20&lt;/span&gt;&lt;span class="c1"&gt;#device_tree_address=0x03000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-21"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;21&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;vmlinuz&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-22"&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="na"&gt;initramfs initrd.img followkernel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span id="line-23"&gt;&lt;span class="linenos"&gt;23&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-24"&gt;&lt;span class="linenos"&gt;24&lt;/span&gt;&lt;span class="c1"&gt;# The following settings are &amp;quot;defaults&amp;quot; expected to be overridden by the&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-25"&gt;&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c1"&gt;# included configuration. The only reason they are included is, again, to&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-26"&gt;&lt;span class="linenos"&gt;26&lt;/span&gt;&lt;span class="c1"&gt;# support old firmwares which don&amp;#39;t understand the &amp;quot;include&amp;quot; command.&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-27"&gt;&lt;span class="linenos"&gt;27&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-28"&gt;&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="na"&gt;enable_uart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-29"&gt;&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-30"&gt;&lt;span class="linenos"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-31"&gt;&lt;span class="linenos"&gt;31&lt;/span&gt;&lt;span class="na"&gt;include syscfg.txt&lt;/span&gt;
&lt;/span&gt;&lt;span id="line-32"&gt;&lt;span class="linenos"&gt;32&lt;/span&gt;&lt;span class="na"&gt;include usercfg.txt&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Obviously if you want to undo the changes you can simply comment out the lines
you&amp;#8217;ve added and uncomment the ones you commented out (one of the joys of
textual configuration being that leaving obsolete stuff commented out for
reversion is&amp;nbsp;trivial!).&lt;/p&gt;
&lt;p&gt;Save your changes, and &lt;strong&gt;safely remove&lt;/strong&gt; your &lt;span class="caps"&gt;USB&lt;/span&gt; enclosure however that&amp;#8217;s done
in your particular &lt;span class="caps"&gt;OS&lt;/span&gt; (&lt;em&gt;don&amp;#8217;t just unplug it&lt;/em&gt;, you need to make sure
everything&amp;#8217;s unmounted&amp;nbsp;cleanly).&lt;/p&gt;
&lt;img alt="An oddly angled picture captured to show the initial login screen of Ubuntu Server 20.04.2, and the USB-connected SSD underneath the Pi 4 it booted from. The SSD enclosure is labelled and the words &amp;quot;Ubuntu 20.04.2 LTS&amp;quot; on the screen are distorted for effect." src="https://waldorf.waveform.org.uk/images/focal-on-usb.jpg" /&gt;
&lt;p&gt;And you&amp;#8217;re done! Connect the enclosure to a free &lt;span class="caps"&gt;USB3&lt;/span&gt; port on your Pi 4 and you
should be good to&amp;nbsp;go.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="ubuntu"></category><category term="u-boot"></category><category term="boot"></category><category term="usb"></category></entry><entry><title>The Pins They Are A-Changin’</title><link href="https://waldorf.waveform.org.uk/2021/the-pins-they-are-a-changin.html" rel="alternate"></link><published>2021-04-20T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-04-20:/2021/the-pins-they-are-a-changin.html</id><summary type="html">&lt;p class="first last"&gt;A look at the kernel level changes in accessing the &lt;span class="caps"&gt;GPIO&lt;/span&gt; pins on the
Raspberry&amp;nbsp;Pi&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2022/the-one-where-dave-breaks-stuff.html"&gt;Breaking &lt;span class="caps"&gt;GPIO&lt;/span&gt;(ish)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some fairly major (and frankly rather overdue) changes are coming in the way
that users access the &lt;span class="caps"&gt;GPIO&lt;/span&gt; pins on the Raspberry Pi. The shift will (hopefully)
be fairly gradual and, for users of certain libraries, entirely invisible.
However, for others they may be quite disruptive. As forewarned is forearmed,
hopefully this little article may be of some use to those&amp;nbsp;affected!&lt;/p&gt;
&lt;p&gt;I really must stop writing &amp;#8220;little&amp;#8221; or &amp;#8220;quick&amp;#8221; in a desperate attempt to
persuade my writing tendencies toward brevity … it never works … my humble
apologies, dear reader, for the extended ramble you are about to&amp;nbsp;endure.&lt;/p&gt;
&lt;div class="section" id="ancient-history"&gt;
&lt;h2&gt;Ancient&amp;nbsp;History&lt;/h2&gt;
&lt;p&gt;Our story begins in antiquity, which is to say Linux kernel version 3.16,
circa 2014. The Raspberry Pi is growing in popularity and everyone is keen to
use these new (to the &lt;span class="caps"&gt;PC&lt;/span&gt; world anyway) things called&amp;nbsp;&amp;#8220;pins&amp;#8221;.&lt;/p&gt;
&lt;p&gt;The simple, neat theory of hardware access is that a kernel mediates all access
to your hardware, and your program interacts with kernel APIs of some form to
&lt;em&gt;indirectly&lt;/em&gt; use that hardware. For the &lt;span class="caps"&gt;GPIO&lt;/span&gt; pins on the Pi, this meant the
&lt;strong&gt;&lt;span class="caps"&gt;GPIO&lt;/span&gt; sysfs&lt;/strong&gt; interface. This is still found today under the
&lt;tt class="docutils literal"&gt;/sys/class/gpio&lt;/tt&gt; path in the file-system. The two important paths under here
are &lt;tt class="docutils literal"&gt;export&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;unexport&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s say you have an &lt;span class="caps"&gt;LED&lt;/span&gt; on &lt;span class="caps"&gt;GPIO&lt;/span&gt; 26 that you want to control, like&amp;nbsp;so:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/led-output_bb.svg" type="image/svg+xml"&gt;Breadboard diagram illustrating an &lt;span class="caps"&gt;LED&lt;/span&gt; and a 330Ω resistor connected
in series between &lt;span class="caps"&gt;GPIO&lt;/span&gt; 26 (on pin 37) and ground (on pin 39)&lt;/object&gt;
&lt;p&gt;You can do so via simple file-system operations (the following assumes you have
write access to this area of the file-system, which is the case for the default
user on RaspiOS, but not Ubuntu so switch to root if you want to try it&amp;nbsp;there):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/sys/class/gpio&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;export  gpiochip0  gpiochip504  unexport
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;26&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;export  gpio26  gpiochip0  gpiochip504  unexport
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;gpio26&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;active_low  device  direction  edge  power  subsystem  uevent  value&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;As we can see, writing &amp;#8220;26&amp;#8221; to &lt;tt class="docutils literal"&gt;export&lt;/tt&gt; has magically created a new
&lt;tt class="docutils literal"&gt;gpio26&lt;/tt&gt; path that has various files under it which we can use to manipulate
the &lt;span class="caps"&gt;GPIO&lt;/span&gt;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;gpio26/direction&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;in
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;gpio26/direction&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;gpio26/value&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;0
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;gpio26/value
&lt;/pre&gt;
&lt;p&gt;Presto! At this point, your &lt;span class="caps"&gt;LED&lt;/span&gt; will be&amp;nbsp;lit.&lt;/p&gt;
&lt;p&gt;As you may guess, you can change the &lt;span class="caps"&gt;GPIO&lt;/span&gt; back into an input by writing &amp;#8220;in&amp;#8221; to
&lt;tt class="docutils literal"&gt;gpio26/direction&lt;/tt&gt;. You can read &lt;tt class="docutils literal"&gt;gpio26/value&lt;/tt&gt; to determine the value of
the input. You can perform edge detection by writing values to the
&lt;tt class="docutils literal"&gt;gpio26/edge&lt;/tt&gt; file, and so&amp;nbsp;on.&lt;/p&gt;
&lt;p&gt;This may look simple and neat, but in practice there&amp;#8217;s a lot of problems with
it. At the root of most of those problems is the issue that the file-system is
a global resource, so this &lt;span class="caps"&gt;API&lt;/span&gt; provides no concept of&amp;nbsp;&amp;#8220;ownership&amp;#8221;.&lt;/p&gt;
&lt;p&gt;If process A and process B both have write access to the &lt;span class="caps"&gt;GPIO&lt;/span&gt; sysfs interface,
both can export &lt;span class="caps"&gt;GPIO&lt;/span&gt; 26. What&amp;#8217;s stopping them both from accessing &lt;span class="caps"&gt;GPIO&lt;/span&gt; 26 at
the same&amp;nbsp;time?&lt;/p&gt;
&lt;p&gt;Nothing.&lt;/p&gt;
&lt;p&gt;Can this be worked around with file-system permissions? Sadly, it&amp;#8217;s not that
simple: what process sets those permissions? How do you avoid race conditions
when changing permissions of multiple file-system&amp;nbsp;entries?&lt;/p&gt;
&lt;p&gt;For instance, on RaspiOS, to permit &lt;span class="caps"&gt;GPIO&lt;/span&gt; access to ordinary users, rules were
added that change the ownership of these files to permit members of the &amp;#8220;gpio&amp;#8221;
group to access them. However, that change isn&amp;#8217;t instantaneous when the
&lt;tt class="docutils literal"&gt;gpio26&lt;/tt&gt; path is created, so all &lt;span class="caps"&gt;GPIO&lt;/span&gt; libraries relying on it have hacks that
look something&amp;nbsp;like:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Check if &lt;tt class="docutils literal"&gt;gpio26&lt;/tt&gt; exists; if it does,&amp;nbsp;exit&lt;/li&gt;
&lt;li&gt;Write &amp;#8220;26&amp;#8221; to &lt;tt class="docutils literal"&gt;gpio/export&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Check &lt;tt class="docutils literal"&gt;gpio26&lt;/tt&gt; exists&lt;/li&gt;
&lt;li&gt;Check &lt;tt class="docutils literal"&gt;gpio26/direction&lt;/tt&gt; is&amp;nbsp;writable&lt;/li&gt;
&lt;li&gt;If not, wait a few milliseconds and try&amp;nbsp;again&lt;/li&gt;
&lt;li&gt;If after, say, 1 second we still can&amp;#8217;t write to it, assume we&amp;#8217;ll never be
able to and throw an&amp;nbsp;error&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Urgh! You can see what this looks like in gpiozero&amp;#8217;s &amp;#8220;native&amp;#8221; driver under
the &lt;a class="reference external" href="https://github.com/gpiozero/gpiozero/blob/f203f4abe0e11c5f82ef952370ddebca5a02bf0c/gpiozero/pins/native.py#L235"&gt;&lt;span class="caps"&gt;GPIOFS&lt;/span&gt;.export&lt;/a&gt; method (full disclosure: this part of gpiozero is largely
my&amp;nbsp;fault).&lt;/p&gt;
&lt;p&gt;It gets worse&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;Due to that first check, if the &lt;span class="caps"&gt;GPIO&lt;/span&gt; is &lt;em&gt;already&lt;/em&gt; exported, our only option is
to go ahead and use it. Maybe it&amp;#8217;s left over from something that crashed before
it could un-export it? Maybe it&amp;#8217;s currently in use by another process? We can&amp;#8217;t
know, so we just barge in and use it; a library could throw a warning but
there&amp;#8217;s precious little a user could &lt;em&gt;do&lt;/em&gt; in response to that&amp;nbsp;warning.&lt;/p&gt;
&lt;p&gt;And there&amp;#8217;s another problem: this interface is slow. Sufficiently slow that
back in the Pi 1 days, it was considered effectively useless for Pulse Width
Modulation (&lt;span class="caps"&gt;PWM&lt;/span&gt;). This is a technique of rapidly pulsing a pin which is
commonly used to provide variable speed control on motors, position information
for servos,&amp;nbsp;etc.&lt;/p&gt;
&lt;p&gt;What&amp;#8217;s faster than&amp;nbsp;sysfs?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="how-the-wave-was-done"&gt;
&lt;h2&gt;How The Wave Was&amp;nbsp;Done&lt;/h2&gt;
&lt;p&gt;If the sysfs interface was too slow, and the Linux kernel (prior to 4.8)
provided no alternative … how &lt;em&gt;did&lt;/em&gt; userspace (that is, &amp;#8220;not kernel&amp;#8221;) processes
manage &lt;span class="caps"&gt;PWM&lt;/span&gt; back in the Pi 1 days? There were two&amp;nbsp;options:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Do things the Right Way™, with hardware! The Pi provides a hardware driven
&lt;span class="caps"&gt;PWM&lt;/span&gt; port capable of fantastic speeds. Because it&amp;#8217;s hardware driven it also
doesn&amp;#8217;t use any &lt;span class="caps"&gt;CPU&lt;/span&gt; time and is wonderfully stable as it&amp;#8217;s not interrupted
by things like I/O.&amp;nbsp;Perfect!&lt;/p&gt;
&lt;p&gt;Unfortunately it&amp;#8217;s only available on a single pin (actually on modern Pis
there&amp;#8217;s a choice of 4 pins, but there&amp;#8217;s still only two hardware &lt;span class="caps"&gt;PWM&lt;/span&gt; channels
you can&amp;nbsp;use).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Get hacky! Just &lt;strong&gt;bang on the hardware registers&lt;/strong&gt; from userspace! If you&amp;#8217;re
using a compiled language like C, you can still go pretty damned fast, and
you can use whichever and however many pins you want (provided the &lt;span class="caps"&gt;CPU&lt;/span&gt; can
keep&amp;nbsp;up).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Take a wild guess which one got&amp;nbsp;used!&lt;/p&gt;
&lt;p&gt;To be clear, I don&amp;#8217;t blame anyone for this choice. The limitation of a single
pin for hardware &lt;span class="caps"&gt;PWM&lt;/span&gt; was a major problem. What if you have &lt;em&gt;two&lt;/em&gt; servos to
control (pitch and yaw, say)? The &amp;#8220;correct&amp;#8221; answer is &amp;#8220;go buy more hardware
like an I²C servo driver&amp;#8221; but writing code is often cheaper, and faster than
waiting for the&amp;nbsp;postman.&lt;/p&gt;
&lt;p&gt;But hang on a tick … doesn&amp;#8217;t the kernel control the hardware? How &lt;em&gt;does&lt;/em&gt; a
userspace application get to bang on hardware registers?! Simple! Get root
privileges, find the relevant area for the &lt;span class="caps"&gt;GPIO&lt;/span&gt; registers in &lt;tt class="docutils literal"&gt;/dev/mem&lt;/tt&gt; (a
&amp;#8220;special file&amp;#8221; that represents all the physical memory in your machine) and
write to&amp;nbsp;it!&lt;/p&gt;
&lt;p&gt;This is, shall we say, &lt;em&gt;not&lt;/em&gt; what userspace programs are meant to do. Things
got &lt;em&gt;slightly&lt;/em&gt; more respectable along the way, of course (they had to!).
Running everything as root was a problem that was solved by producing a device
called &lt;tt class="docutils literal"&gt;/dev/gpiomem&lt;/tt&gt; which simply gave access to the portion of memory that
contained the &lt;span class="caps"&gt;GPIO&lt;/span&gt; registers and nothing else. And numerous libraries for
numerous languages sprung up to paper over these horrors and present a nice
user-friendly &lt;span class="caps"&gt;GPIO&lt;/span&gt; interface. Still, if all this sounds &lt;em&gt;incredibly&lt;/em&gt; hacky …
well, that&amp;#8217;s because it&amp;nbsp;was.&lt;/p&gt;
&lt;p&gt;Or rather&amp;nbsp;is.&lt;/p&gt;
&lt;p&gt;Yes, dear reader, this is &lt;em&gt;still&lt;/em&gt; how the vast majority of &lt;span class="caps"&gt;GPIO&lt;/span&gt; control on the
Pi is done on at the time of writing. This is how &lt;a class="reference external" href="https://github.com/WiringPi/WiringPi/blob/f66c883d7c75280971a01619cd503d1809754801/wiringPi/wiringPi.c#L2329"&gt;wiringPi&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/joan2937/pigpio/blob/c33738a320a3e28824af7807edafda440952c05d/pigpio.c#L7360"&gt;pigpio&lt;/a&gt;,
&lt;a class="reference external" href="https://sourceforge.net/p/raspberry-gpio-python/code/ci/0.7.0/tree/source/c_gpio.c#l78"&gt;RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;&lt;/a&gt;, and gpiozero&amp;#8217;s &amp;#8220;&lt;a class="reference external" href="https://github.com/gpiozero/gpiozero/blob/f203f4abe0e11c5f82ef952370ddebca5a02bf0c/gpiozero/pins/native.py#L156"&gt;native&lt;/a&gt;&amp;#8221; driver all operate (yes kids, the
latter is pure Python banging directly on hardware registers; you don&amp;#8217;t have to
learn C just to abuse&amp;nbsp;stuff!).&lt;/p&gt;
&lt;p&gt;Doesn&amp;#8217;t stomping all over the registers under the kernel&amp;#8217;s nose cause any
issues? Especially considering the kernel&amp;#8217;s also trying to control those same
pins via the sysfs interface, using those same&amp;nbsp;registers?&lt;/p&gt;
&lt;p&gt;Well … yes&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-bleeding-edge"&gt;
&lt;h2&gt;The Bleeding&amp;nbsp;Edge&lt;/h2&gt;
&lt;p&gt;Almost from the start it became apparent that attempting to perform &lt;strong&gt;edge
detection&lt;/strong&gt; (nothing to do with images; this is simply responding to the rising
or falling &amp;#8220;edge&amp;#8221; of a pulse on a pin) didn&amp;#8217;t work with the registers. This was
one area where the Linux kernel exerted absolute&amp;nbsp;control.&lt;/p&gt;
&lt;p&gt;You could configure pins and drive them high and low quite happily without the
kernel complaining, but mess with the edge detection registers and stuff
started&amp;nbsp;crashing.&lt;/p&gt;
&lt;p&gt;Ultimately this meant that most libraries wound up being rather schizophrenic.
They&amp;#8217;d &amp;#8220;play nice&amp;#8221; and use the sysfs interface for edge detection, but then
stomp all over the hardware registers whenever they wanted to do anything else
(reading the value of a &lt;span class="caps"&gt;GPIO&lt;/span&gt;, setting its direction, writing a value,&amp;nbsp;etc.).&lt;/p&gt;
&lt;p&gt;And until very recently, this has worked happily&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="as-neat-as-a-pin"&gt;
&lt;h2&gt;As Neat as a&amp;nbsp;Pin&lt;/h2&gt;
&lt;p&gt;Time rolls on, the seasons change, and Linux kernel 4.8 is&amp;nbsp;released.&lt;/p&gt;
&lt;p&gt;This kernel introduced a new &amp;#8220;gpiochip&amp;#8221; device file (e.g. &lt;tt class="docutils literal"&gt;/dev/gpiochip0&lt;/tt&gt;).
Such files have a set of ioctls (&amp;#8220;special&amp;#8221; commands for special files) that
provide the means to manipulate the GPIOs, and this is the basis of the new
&lt;span class="caps"&gt;API&lt;/span&gt; for userspace control of the&amp;nbsp;pins.&lt;/p&gt;
&lt;p&gt;By far the most important change is that now there&amp;#8217;s a concept of &amp;#8220;ownership&amp;#8221;.
Before your process can &lt;em&gt;do&lt;/em&gt; anything with a &lt;span class="caps"&gt;GPIO&lt;/span&gt; it has to request access to
it (via an ioctl). In doing so, the kernel will track your process&amp;#8217; use of that
&lt;span class="caps"&gt;GPIO&lt;/span&gt;, and won&amp;#8217;t permit any other process to use it simultaneously. Should your
process terminate without releasing it, the kernel will notice and free up the
ownership&amp;nbsp;automatically.&lt;/p&gt;
&lt;p&gt;So far, so&amp;nbsp;good.&lt;/p&gt;
&lt;p&gt;A userspace library (&lt;a class="reference external" href="https://wiki.loliot.net/docs/lang/python/libraries/gpiod/python-gpiod-about"&gt;libgpiod&lt;/a&gt;) exists to provide a friendly interface to all
the ioctls for the gpiochip device, and it comes with a few nice command line
utilities like &lt;tt class="docutils literal"&gt;gpioinfo&lt;/tt&gt; which can be used to determine what GPIOs exist and
whether they are currently in use by anything. You can get all this by
installing the &amp;#8220;gpiod&amp;#8221; package. For&amp;nbsp;example:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;gpiod&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... all the usual apt stuff ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;gpioinfo&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;gpiochip0 - 58 lines:
        line   0:     &amp;quot;ID_SDA&amp;quot;       unused   input  active-high
        line   1:     &amp;quot;ID_SCL&amp;quot;       unused   input  active-high
        line   2:       &amp;quot;SDA1&amp;quot;       unused   input  active-high
        line   3:       &amp;quot;SCL1&amp;quot;       unused   input  active-high
        line   4:  &amp;quot;GPIO_GCLK&amp;quot;       unused   input  active-high
        line   5:      &amp;quot;GPIO5&amp;quot;       unused   input  active-high
        line   6:      &amp;quot;GPIO6&amp;quot;       unused   input  active-high
        line   7:  &amp;quot;SPI_CE1_N&amp;quot;   &amp;quot;spi0 CS1&amp;quot;  output   active-low [used]
        line   8:  &amp;quot;SPI_CE0_N&amp;quot;   &amp;quot;spi0 CS0&amp;quot;  output   active-low [used]
        line   9:   &amp;quot;SPI_MISO&amp;quot;       unused   input  active-high
        line  10:   &amp;quot;SPI_MOSI&amp;quot;       unused   input  active-high
        line  11:   &amp;quot;SPI_SCLK&amp;quot;       unused   input  active-high
        line  12:     &amp;quot;GPIO12&amp;quot;       unused   input  active-high
        line  13:     &amp;quot;GPIO13&amp;quot;       unused  output  active-high
        line  14:       &amp;quot;TXD1&amp;quot;       unused   input  active-high
        line  15:       &amp;quot;RXD1&amp;quot;       unused   input  active-high
        line  16:     &amp;quot;GPIO16&amp;quot;       unused   input  active-high
        line  17:     &amp;quot;GPIO17&amp;quot;       unused   input  active-high
        line  18:     &amp;quot;GPIO18&amp;quot; &amp;quot;gpio-fan&amp;#64;0&amp;quot;  output  active-high [used]
        line  19:     &amp;quot;GPIO19&amp;quot;       unused   input  active-high
        line  20:     &amp;quot;GPIO20&amp;quot;       unused   input  active-high
        line  21:     &amp;quot;GPIO21&amp;quot;       unused   input  active-high
        line  22:     &amp;quot;GPIO22&amp;quot;       unused   input  active-high
        line  23:     &amp;quot;GPIO23&amp;quot;       unused   input  active-high
        line  24:     &amp;quot;GPIO24&amp;quot;       unused   input  active-high
        line  25:     &amp;quot;GPIO25&amp;quot;       unused   input  active-high
        line  26:     &amp;quot;GPIO26&amp;quot;      &amp;quot;sysfs&amp;quot;   input  active-high [used]
        line  27:     &amp;quot;GPIO27&amp;quot;       unused   input  active-high
        line  28: &amp;quot;RGMII_MDIO&amp;quot;       unused   input  active-high
        line  29:  &amp;quot;RGMIO_MDC&amp;quot;       unused   input  active-high
        line  30:       &amp;quot;CTS0&amp;quot;       unused   input  active-high
        line  31:       &amp;quot;RTS0&amp;quot;       unused   input  active-high
        line  32:       &amp;quot;TXD0&amp;quot;       unused   input  active-high
        line  33:       &amp;quot;RXD0&amp;quot;       unused   input  active-high
        line  34:    &amp;quot;SD1_CLK&amp;quot;       unused   input  active-high
        line  35:    &amp;quot;SD1_CMD&amp;quot;       unused   input  active-high
        line  36:  &amp;quot;SD1_DATA0&amp;quot;       unused   input  active-high
        line  37:  &amp;quot;SD1_DATA1&amp;quot;       unused   input  active-high
        line  38:  &amp;quot;SD1_DATA2&amp;quot;       unused   input  active-high
        line  39:  &amp;quot;SD1_DATA3&amp;quot;       unused   input  active-high
        line  40:  &amp;quot;PWM0_MISO&amp;quot;       unused   input  active-high
        line  41:  &amp;quot;PWM1_MOSI&amp;quot;       unused   input  active-high
        line  42: &amp;quot;STATUS_LED_G_CLK&amp;quot; &amp;quot;led0&amp;quot; output active-high [used]
        line  43: &amp;quot;SPIFLASH_CE_N&amp;quot; unused input active-high
        line  44:       &amp;quot;SDA0&amp;quot;       unused   input  active-high
        line  45:       &amp;quot;SCL0&amp;quot;       unused   input  active-high
        line  46: &amp;quot;RGMII_RXCLK&amp;quot; unused input active-high
        line  47: &amp;quot;RGMII_RXCTL&amp;quot; unused input active-high
        line  48: &amp;quot;RGMII_RXD0&amp;quot;       unused   input  active-high
        line  49: &amp;quot;RGMII_RXD1&amp;quot;       unused   input  active-high
        line  50: &amp;quot;RGMII_RXD2&amp;quot;       unused   input  active-high
        line  51: &amp;quot;RGMII_RXD3&amp;quot;       unused   input  active-high
        line  52: &amp;quot;RGMII_TXCLK&amp;quot; unused input active-high
        line  53: &amp;quot;RGMII_TXCTL&amp;quot; unused input active-high
        line  54: &amp;quot;RGMII_TXD0&amp;quot;       unused   input  active-high
        line  55: &amp;quot;RGMII_TXD1&amp;quot;       unused   input  active-high
        line  56: &amp;quot;RGMII_TXD2&amp;quot;       unused   input  active-high
        line  57: &amp;quot;RGMII_TXD3&amp;quot;       unused   input  active-high
gpiochip1 - 8 lines:
        line   0:      &amp;quot;BT_ON&amp;quot;       unused  output  active-high
        line   1:      &amp;quot;WL_ON&amp;quot;       unused  output  active-high
        line   2: &amp;quot;PWR_LED_OFF&amp;quot; &amp;quot;led1&amp;quot; output active-low [used]
        line   3: &amp;quot;GLOBAL_RESET&amp;quot; unused output active-high
        line   4: &amp;quot;VDD_SD_IO_SEL&amp;quot; &amp;quot;vdd-sd-io&amp;quot; output active-high [used]
        line   5:   &amp;quot;CAM_GPIO&amp;quot;       unused  output  active-high
        line   6:  &amp;quot;SD_PWR_ON&amp;quot; &amp;quot;sd_vcc_reg&amp;quot;  output  active-high [used]
        line   7:    &amp;quot;SD_OC_N&amp;quot;       unused   input  active-high&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s a whole lotta output! But, hold on! If you look over the list, you&amp;#8217;ll
see that &lt;span class="caps"&gt;GPIO&lt;/span&gt; 26 is apparently &amp;#8220;used&amp;#8221; by &amp;#8220;sysfs&amp;#8221;. Yup, the old interface is
still there, but now it&amp;#8217;s implemented as something on top of the new gpiochip&amp;nbsp;device(s).&lt;/p&gt;
&lt;p&gt;This is actually good news: it means nothing breaks immediately (sort of). The
bad news is: remember how things using sysfs can leave things exported? If
anything does, the user needs to manually unexport them before anything using
the new interface can use them. To be fair, there&amp;#8217;s no good way around this,
and I think this is probably the best compromise possible in the&amp;nbsp;circumstances.&lt;/p&gt;
&lt;p&gt;How would controlling our &lt;span class="caps"&gt;LED&lt;/span&gt; look with libgpiod under&amp;nbsp;Python?&lt;/p&gt;
&lt;pre class="code pycon literal-block"&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gpiod&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;chip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gpiod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gpiod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OPEN_BY_NUMBER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'my-script'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_direction_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;All this looks very promising; the problems with the sysfs interface are dealt
with and there&amp;#8217;s now a simple, neat solution to controlling &lt;span class="caps"&gt;GPIO&lt;/span&gt; pins from
userspace in the form of libgpiod. Which begs the question: if all this came
along in kernel version 4.8, circa 2016, why are the vast majority of userspace
&lt;span class="caps"&gt;GPIO&lt;/span&gt; libraries on the Pi still banging on&amp;nbsp;registers?&lt;/p&gt;
&lt;p&gt;Two reasons: firstly, it&amp;#8217;s still slower than banging on registers. No real
surprise there, but it&amp;#8217;s not &lt;em&gt;much&lt;/em&gt; slower. However, a bigger problem is that
(in the initial version of this new interface, at least), there was still
something missing&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pulling-the-pin"&gt;
&lt;h2&gt;Pulling the&amp;nbsp;Pin&lt;/h2&gt;
&lt;p&gt;&lt;span class="caps"&gt;GPIO&lt;/span&gt; pins are remarkably versatile things. We&amp;#8217;ve seen them acting as an output.
But how about as an input? Imagine we have a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Push-button"&gt;push-button&lt;/a&gt; connected
between &lt;span class="caps"&gt;GPIO&lt;/span&gt; 13 and ground, like&amp;nbsp;so:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/button-input_bb.svg" type="image/svg+xml"&gt;Breadboard diagram adding a momentary push-button connected
between &lt;span class="caps"&gt;GPIO&lt;/span&gt; 13 (on pin 33) and ground (on pin 39) to the earlier
circuit&lt;/object&gt;
&lt;p&gt;How do we go about detecting when this button is&amp;nbsp;pressed?&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;We need the &lt;span class="caps"&gt;GPIO&lt;/span&gt; to be an input. No problem&amp;nbsp;there.&lt;/li&gt;
&lt;li&gt;We need to wait for a falling edge when the button connects the &lt;span class="caps"&gt;GPIO&lt;/span&gt; to
ground. Again, no&amp;nbsp;problem.&lt;/li&gt;
&lt;li&gt;We need the &lt;span class="caps"&gt;GPIO&lt;/span&gt; to be &lt;strong&gt;pulled up&lt;/strong&gt; to ensure there&amp;#8217;s a potential to fall
(or at least enough to register a difference on a &lt;span class="caps"&gt;GPIO&lt;/span&gt; which would otherwise
be&amp;nbsp;floating).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go back and have a look at the sysfs interface we saw earlier. See any
pseudo-file for configuring a&amp;nbsp;pull?&lt;/p&gt;
&lt;p&gt;No, and this was another big reason why all the libraries resorted to banging
on the registers; the kernel interface of the time didn&amp;#8217;t support a fairly
major piece of functionality, and one that is useful in the most basic of
operations like detecting when a switch&amp;nbsp;closes.&lt;/p&gt;
&lt;p&gt;Sadly, the brand new gpiochip interface, &lt;em&gt;also&lt;/em&gt; omitted this functionality in
its first incarnation (and all the way up to the version that shipped with
Debian and RaspiOS &amp;#8220;Buster&amp;#8221;), so even if the myriad &lt;span class="caps"&gt;GPIO&lt;/span&gt; libraries had
&lt;em&gt;wanted&lt;/em&gt; to move to it as an underlying layer, they&amp;#8217;d still have to bang on
registers to implement some of their existing&amp;nbsp;interface.&lt;/p&gt;
&lt;p&gt;C&amp;#8217;est la vie&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pins-and-needles"&gt;
&lt;h2&gt;Pins and&amp;nbsp;Needles&lt;/h2&gt;
&lt;p&gt;Allow me a brief interlude, dear reader, to explore userspace &lt;span class="caps"&gt;GPIO&lt;/span&gt; access from
the perspective of the&amp;nbsp;kernel.&lt;/p&gt;
&lt;p&gt;Accessing GPIOs from userspace is considered an aberration that should never be
tolerated in any end-user product. That statement may sound rather strong, but
here&amp;#8217;s a quote from the kernel&amp;#8217;s &lt;a class="reference external" href="https://www.kernel.org/doc/html/latest/driver-api/gpio/using-gpio.html"&gt;&lt;span class="caps"&gt;GPIO&lt;/span&gt; sub-system documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
The userspace &lt;span class="caps"&gt;ABI&lt;/span&gt; is intended for one-off deployments. &lt;em&gt;[…]&lt;/em&gt; Do not under
any circumstances abuse the &lt;span class="caps"&gt;GPIO&lt;/span&gt; userspace &lt;span class="caps"&gt;ABI&lt;/span&gt; to cut corners in any
product development projects. If you use it for prototyping, then do not
productify &lt;em&gt;[sic]&lt;/em&gt; the prototype: rewrite it using proper kernel drivers.
Do not under any circumstances deploy any uniform products using &lt;span class="caps"&gt;GPIO&lt;/span&gt; from
userspace.&lt;/blockquote&gt;
&lt;p&gt;If the Kernel Gods hold my work in disfavour, I can only plead that gpiozero is
a library for educators and makers, not mass-produced&amp;nbsp;products.&lt;/p&gt;
&lt;p&gt;Furthermore, I have some considerable sympathy with this view. GPIOs are a
means to an end, and userspace should be concerned with the &amp;#8220;end&amp;#8221;, not the
&amp;#8220;means&amp;#8221;. In fact this is a large part of gpiozero&amp;#8217;s philosophy: that by
concentrating on the things attached to pins (like LEDs and buttons) rather
than the pins themselves, everything becomes much simpler (I&amp;#8217;d love to claim
credit for this stroke of genius, but it was another of Ben&amp;#8217;s&amp;nbsp;ideas).&lt;/p&gt;
&lt;p&gt;Still, gpiozero &lt;em&gt;is&lt;/em&gt; a userspace &lt;span class="caps"&gt;GPIO&lt;/span&gt; library, so perhaps I&amp;#8217;m damned
regardless. In the course of my background research for this article, I did
come across &lt;a class="reference external" href="https://elinux.org/images/9/9b/GPIO_for_Engineers_and_Makers.pdf"&gt;a presentation (&lt;span class="caps"&gt;PDF&lt;/span&gt;)&lt;/a&gt; covering the introduction of the gpiochip
interface which included &amp;#8220;The Rules of Linux Userspace &lt;span class="caps"&gt;GPIO&lt;/span&gt;&amp;#8221;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;You do not access GPIOs from&amp;nbsp;userspace&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;YOU&lt;/span&gt; &lt;span class="caps"&gt;DO&lt;/span&gt; &lt;span class="caps"&gt;NOT&lt;/span&gt; &lt;span class="caps"&gt;ACCESS&lt;/span&gt; &lt;span class="caps"&gt;GPIOS&lt;/span&gt; &lt;span class="caps"&gt;FROM&lt;/span&gt; &lt;span class="caps"&gt;USERSPACE&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Read &lt;a class="reference external" href="https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git/tree/Documentation/gpio/drivers-on-gpio.txt?h=v4.7"&gt;Documentation/gpio/drivers-on-gpio.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use the character device &lt;em&gt;[/dev/gpiochipN]&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;And now I&amp;#8217;m picturing Tyler Durden railing against the kids at a Raspberry Jam
for breaking rule #1&amp;nbsp;:-)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="bang-up-to-date"&gt;
&lt;h2&gt;Bang Up To&amp;nbsp;Date&lt;/h2&gt;
&lt;p&gt;Anyhow, that brings us to the present day. Ubuntu Hirsute Hippo is releasing
shortly (a couple of days away as I write this), and will ship with Linux
kernel version 5.11. This has a gpiochip device (and libgpiod) which &lt;em&gt;does&lt;/em&gt;
support setting pulls, and is therefore (mostly) capable of doing everything
the traditional &lt;span class="caps"&gt;GPIO&lt;/span&gt; libraries&amp;nbsp;do.&lt;/p&gt;
&lt;p&gt;Hang on …&amp;nbsp;&amp;#8220;mostly&amp;#8221;?&lt;/p&gt;
&lt;p&gt;Unfortunately, software &lt;span class="caps"&gt;PWM&lt;/span&gt; is still missing from this. But fret not! The
creator of one of the traditional &lt;span class="caps"&gt;GPIO&lt;/span&gt; libraries (pigpio) has produced another
&lt;span class="caps"&gt;GPIO&lt;/span&gt; library built upon the new gpiochip device called &amp;#8220;&lt;a class="reference external" href="http://abyz.me.uk/lg/"&gt;lg&lt;/a&gt;&amp;#8220;. This is a well
and truly feature-complete library covering all the &lt;span class="caps"&gt;GPIO&lt;/span&gt; operations discussed
above (including a very full-featured &lt;span class="caps"&gt;PWM&lt;/span&gt; implementation), &lt;em&gt;and&lt;/em&gt; covering the
&lt;span class="caps"&gt;SPI&lt;/span&gt;, I²C, and &lt;span class="caps"&gt;UART&lt;/span&gt; kernel&amp;nbsp;devices.&lt;/p&gt;
&lt;p&gt;This will be include in Ubuntu Hirsute with the&amp;nbsp;packages:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;liblgpio1&lt;/tt&gt; — the C&amp;nbsp;library&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python3-lgpio&lt;/span&gt;&lt;/tt&gt; — the Python 3 bindings for&amp;nbsp;liblgpio&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It will also be included in RaspiOS Buster shortly (I can only apologise I
haven&amp;#8217;t sent the packaging for this yet, but last minute release debugging has
swamped me! I &lt;em&gt;will&lt;/em&gt; get to it shortly!), and is a fine interface to migrate
your &lt;span class="caps"&gt;GPIO&lt;/span&gt; projects to if you&amp;#8217;re currently using one of the traditional
libraries like RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Why should you&amp;nbsp;migrate?&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The sysfs interface has been deprecated for a &lt;em&gt;long&lt;/em&gt; time and will disappear
at some&amp;nbsp;point.&lt;/li&gt;
&lt;li&gt;Fighting the kernel for control of the &lt;span class="caps"&gt;GPIO&lt;/span&gt; registers is &lt;em&gt;not&lt;/em&gt; guaranteed to&amp;nbsp;work.&lt;/li&gt;
&lt;li&gt;There are a few more changes coming to the gpiochip interface, and we can&amp;#8217;t
guarantee that libraries that entirely bypass the kernel will keep working
in the face of those&amp;nbsp;changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Migration is always a pain, but lg has good &lt;a class="reference external" href="http://abyz.me.uk/lg/py_lgpio.html"&gt;documentation&lt;/a&gt; (considerably more
than libgpiod does as far as I can tell), and plenty of &lt;a class="reference external" href="http://abyz.me.uk/lg/examples.html"&gt;examples&lt;/a&gt;. For some
more lg examples, you should also head over to my colleague &lt;a class="reference external" href="https://blogjawn.stufftoread.com/"&gt;William&amp;#8217;s site&lt;/a&gt;
where he&amp;#8217;s put together a nice &lt;a class="reference external" href="https://blogjawn.stufftoread.com/lgpio-tutorial.html"&gt;lgpio tutorial post&lt;/a&gt; using lgpio on Hirsute
(and say &amp;#8220;hi&amp;#8221; — he&amp;#8217;s new to the team, but he&amp;#8217;s been a huge help during the
Hirsute&amp;nbsp;cycle!).&lt;/p&gt;
&lt;p&gt;However, if you&amp;#8217;re using gpiozero&amp;nbsp;…&lt;/p&gt;
&lt;img alt="Professor Farnsworth from Futurama with the caption &amp;quot;Good News Everyone!&amp;quot;" src="https://waldorf.waveform.org.uk/images/good-news.jpg" /&gt;
&lt;p&gt;You shouldn&amp;#8217;t need to migrate at all. The gpiozero library itself has
traditionally defaulted to using RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; as its underlying &amp;#8220;pin driver&amp;#8221;. In
Ubuntu Hirsute (and probably RaspiOS Bullseye when it releases) that underlying
implementation will default to lg instead (falling back to RPi.&lt;span class="caps"&gt;GPIO&lt;/span&gt; if lg isn&amp;#8217;t
found), so you shouldn&amp;#8217;t need to do anything (but obviously if you do notice
any issues, please &lt;a class="reference external" href="https://github.com/gpiozero/gpiozero/issues"&gt;open a ticket&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Finally, for those users of pigpio&amp;#8217;s remote socket support, it&amp;#8217;s worth noting
that the &amp;#8220;lg&amp;#8221; project also&amp;nbsp;includes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;rgpiod&lt;/tt&gt; — a daemon using the lgpio library to provide a (potentially remote)
socket based interface to the &lt;span class="caps"&gt;GPIO&lt;/span&gt;&amp;nbsp;header&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;librgpio1&lt;/tt&gt; — the C client library for rgpiod (largely the same interface as
lgpio but talks to an rgpiod instance instead of the local gpiochip&amp;nbsp;device)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python3-rgpio&lt;/span&gt;&lt;/tt&gt; — the Python 3 bindings for&amp;nbsp;librgpio&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;rgpio-tools&lt;/span&gt;&lt;/tt&gt; — includes &amp;#8220;rgs&amp;#8221;, a shell utility for talking to&amp;nbsp;rgpiod&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="gpio"></category><category term="gpiozero"></category></entry><entry><title>6 Months with the Pi Desktop</title><link href="https://waldorf.waveform.org.uk/2021/6-months-with-the-pi-desktop.html" rel="alternate"></link><published>2021-04-17T00:00:00+01:00</published><updated>2022-09-06T13:27:19+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-04-17:/2021/6-months-with-the-pi-desktop.html</id><summary type="html">&lt;p class="first last"&gt;A run-down of using a Pi as my only desktop for an entire release&amp;nbsp;cycle&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: A look at the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2022/making-jammy-less-dodgy.html"&gt;Jammy desktop image&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In a deep movie trailer voice: &amp;#8220;&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/a-week-with-the-pi-desktop.html"&gt;Previously on Dave&amp;#8217;s blog&lt;/a&gt;&amp;#8220;… &amp;#8220;Oh no! My &lt;span class="caps"&gt;PC&lt;/span&gt; is
no more! Whatever shall I do?!&amp;#8221; &amp;#8220;Oooh, a Pi 400&amp;nbsp;…&amp;#8221;&lt;/p&gt;
&lt;p&gt;Well, it&amp;#8217;s been about 6 months since I started &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food"&gt;dog-fooding&lt;/a&gt; the Ubuntu
desktop on a Raspberry Pi (and since I last managed to write a blog post …
ahem). I&amp;#8217;d originally intended to go back to using a big, fat, Watt-guzzling &lt;span class="caps"&gt;PC&lt;/span&gt;
as my main development machine at some point in this cycle. However, a
combination of global silicon shortages (if the damned crypto-miners could let
me buy a single decent graphics card that&amp;#8217;d be … nice?), and some major
improvements in the Pi desktop meant I wound up spending the &lt;em&gt;entire&lt;/em&gt; hirsute
cycle using the little Pi 400 as my primary&amp;nbsp;desktop.&lt;/p&gt;
&lt;div class="section" id="the-hippopotamus-in-the-room"&gt;
&lt;h2&gt;The Hippopotamus in the&amp;nbsp;Room&lt;/h2&gt;
&lt;p&gt;The biggest issue with this, as discussed in the previous installment, was
video playback. We didn&amp;#8217;t have video acceleration on the Ubuntu Desktop for Pi
and this meant that, at the start of the hirsute development cycle, I still had
to resort to my little laptop (a tiny Celeron powered thing) for video
conferencing. However, at some point (a couple of months ago? I forget the
precise timing as all sorts of work-arounds have been required at one stage or
another) things came together, the planets aligned, the Gods of the Desktop
smiled upon their lowly worshippers and Wayland+&lt;span class="caps"&gt;KMS&lt;/span&gt; started&amp;nbsp;working.&lt;/p&gt;
&lt;p&gt;Suddenly … things got &lt;em&gt;much&lt;/em&gt;&amp;nbsp;better.&lt;/p&gt;
&lt;p&gt;For the past several weeks I&amp;#8217;ve been using my little Pi 400 as my &lt;em&gt;sole&lt;/em&gt;
desktop. The laptop has been ditched entirely. &lt;span class="caps"&gt;HD&lt;/span&gt; YouTube playback works
happily. Video conferencing over Google Meet works perfectly (no Zoom, but
that&amp;#8217;s because there&amp;#8217;s no &lt;span class="caps"&gt;ARM&lt;/span&gt; builds of the client, ho hum), and the desktop
generally feels responsive and&amp;nbsp;slick.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s definitely &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/mutter/+bug/1924251"&gt;a few kinks&lt;/a&gt; still to work out, but nothing show-stopping
that&amp;#8217;s made me dig the laptop out of storage or prevented me from Getting
Stuff&amp;nbsp;Done.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="making-hippos-fly"&gt;
&lt;h2&gt;Making Hippos&amp;nbsp;Fly&lt;/h2&gt;
&lt;img alt="A Pi 400 in front of a simple HDMI monitor with a Logitech C920 webcam attached, and a USB3 SSD resting on the base, surrounded by the typical junk on Dave's desk (myriad cables, a solder station, a spare keyboard for testing, random SD cards)" src="https://waldorf.waveform.org.uk/images/hairy-pi.jpg" /&gt;
&lt;p&gt;Above is a shot of my little desktop setup. Some optimizations to&amp;nbsp;note:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;I don&amp;#8217;t have a fancy monitor. It&amp;#8217;s bog-standard old 1080p &lt;span class="caps"&gt;HDMI&lt;/span&gt; thing.
Personally, I can&amp;#8217;t tell the difference between 1080p and 4K without
squinting through my glasses anyway, but from a performance perspective
having a 1080p monitor means no scaling is required to make the desktop
readable. 200% scaling is fast under Gnome, but the result is frankly too
big for my liking, and fractional scaling causes a noticeable performance
hit. Choose your screen&amp;nbsp;wisely!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Use the &lt;span class="caps"&gt;HDMI0&lt;/span&gt; port. On the Pi 4, it&amp;#8217;s next to the &lt;span class="caps"&gt;USB&lt;/span&gt;-C power port. On the
Pi 400, it&amp;#8217;s the one next to the &lt;span class="caps"&gt;SD&lt;/span&gt; card slot. In certain configurations,
audio playback through &lt;span class="caps"&gt;HDMI&lt;/span&gt; only works from &lt;span class="caps"&gt;HDMI0&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;It&amp;#8217;s booting off an &lt;span class="caps"&gt;SSD&lt;/span&gt; connected to one of the &lt;span class="caps"&gt;USB3&lt;/span&gt; ports (that little
black box sat on the base of the monitor). We introduced support for booting
Ubuntu over &lt;span class="caps"&gt;USB&lt;/span&gt; in groovy, and hirsute should work just as well. Simply
flash the image to your &lt;span class="caps"&gt;USB&lt;/span&gt; drive and you&amp;#8217;re good to go. But do note that
you may need to &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/installing-the-new-pi-ubuntu-desktop.html#usb-hdd-ssd"&gt;enable &lt;span class="caps"&gt;USB&lt;/span&gt; boot&lt;/a&gt; in your Pi&amp;#8217;s boot order first (this is
one-time setup so if your Pi can already boot off &lt;span class="caps"&gt;USB&lt;/span&gt;, you&amp;#8217;re good to&amp;nbsp;go).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;You can&amp;#8217;t attach a Pi Camera Module to the Pi 400, so I&amp;#8217;ve got an old
Logitech C920 attached to one of the &lt;span class="caps"&gt;USB&lt;/span&gt;&amp;nbsp;ports.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The Pi 400 only has &lt;span class="caps"&gt;4GB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt;. For lightweight desktops like &lt;span class="caps"&gt;LXDE&lt;/span&gt; this is
okay, but Gnome shell is quite a bit heavier. The Ubuntu Desktop image for
Pi does come with a &lt;span class="caps"&gt;1GB&lt;/span&gt; swap file but honestly you probably want to extend
this and I highly recommend using zswap (more on that&amp;nbsp;below).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;One advantage the Pi 400 &lt;em&gt;does&lt;/em&gt; have over the Pi 4 is it&amp;#8217;s &lt;em&gt;much&lt;/em&gt; better at
running cool. In turn, this means overclocking it is entirely viable even
without any active cooling. At present I have the following lines added to
my &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; file on the boot partition, and the Pi 400 has been
running with no heat / throttling issues for&amp;nbsp;months:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
arm_freq=2000
over_voltage=6
&lt;/pre&gt;
&lt;p&gt;You &lt;em&gt;might&lt;/em&gt; get away with these settings on a Pi 4, but you&amp;#8217;d likely want
some active cooling for that&amp;nbsp;setup.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="swap-swap-glorious-zswap"&gt;
&lt;h2&gt;Swap, swap, glorious&amp;nbsp;zswap!&lt;/h2&gt;
&lt;p&gt;As a rough rule of thumb, &lt;em&gt;all&lt;/em&gt; systems should have swap. It doesn&amp;#8217;t matter if
your machine is a beast with &lt;span class="caps"&gt;128GB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt;, having &lt;em&gt;some&lt;/em&gt; swap (not much, just a
gig or two, say) is still useful as it permits the kernel to ditch pages that
are never (or extremely rarely) used and use the &lt;span class="caps"&gt;RAM&lt;/span&gt; saved for disk cache (and
there&amp;#8217;s no such thing as too much of&amp;nbsp;that!).&lt;/p&gt;
&lt;p&gt;However, on a Pi 400 running a hefty desktop environment under &lt;span class="caps"&gt;4GB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt; it&amp;#8217;s
downright vital. As mentioned above, the images ship with a &lt;span class="caps"&gt;1GB&lt;/span&gt; swap file (some
may argue it would be preferable to create it on first boot, but it compresses
trivially in the image, and this ensures that when people flash it they
effectively &amp;#8220;reserve&amp;#8221; room for the necessary swap space). Ideally though
(unless you&amp;#8217;re on a beast like the Pi 4 &lt;span class="caps"&gt;8GB&lt;/span&gt; model), you probably want to expand
this to something a bit&amp;nbsp;bigger.&lt;/p&gt;
&lt;p&gt;This can be easily done from the command line but I&amp;#8217;d caution to do this when
you&amp;#8217;ve first booted the system and before you&amp;#8217;ve launched anything else as the
first step is to (temporarily) &lt;em&gt;disable&lt;/em&gt; the swap. The following commands will
add &lt;span class="caps"&gt;1GB&lt;/span&gt; of size to the default swap-file on the desktop&amp;nbsp;image:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo swapoff /swapfile
$ sudo fallocate -l 2g /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;You could trivially add a second swap-file and this wouldn&amp;#8217;t necessitate
disabling the existing one, but that then means getting into editing the
&lt;tt class="docutils literal"&gt;/etc/fstab&lt;/tt&gt; file to add it and this is a bit easier to document. Use
whichever means you are comfortable with, but the important thing here is
that &lt;span class="caps"&gt;1GB&lt;/span&gt; of swap probably isn&amp;#8217;t quite enough on a system with &lt;span class="caps"&gt;4GB&lt;/span&gt; of &lt;span class="caps"&gt;RAM&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Next, I would highly recommend enabling &amp;#8220;&lt;a class="reference external" href="https://www.kernel.org/doc/html/latest/vm/zswap.html"&gt;zswap&lt;/a&gt;&amp;#8220;. This is one of several
forms of virtual memory compression provided by the Linux kernel. The other
commonly used one is &amp;#8220;&lt;a class="reference external" href="https://www.kernel.org/doc/html/latest/admin-guide/blockdev/zram.html"&gt;zram&lt;/a&gt;&amp;#8220;. I&amp;#8217;m not enough of an expert to opine on the
fine detail of these systems, but my rough rule of thumb is as&amp;nbsp;follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;If you have no swap-file at all, zram is preferable. It provides a pseudo
swap-file which is actually a compressed area of &lt;span class="caps"&gt;RAM&lt;/span&gt; and hence requires no
swap-file backing&amp;nbsp;it.&lt;/li&gt;
&lt;li&gt;However, if you have a swap-file, zswap is preferable. It acts as a
write-back cache for the swap-file and can in certain circumstances avoid
pages ever hitting the disk at&amp;nbsp;all.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don&amp;#8217;t worry about the computational overhead of these systems. &lt;span class="caps"&gt;CPU&lt;/span&gt; speed and
memory bandwidth have grown considerably faster than bulk storage &lt;span class="caps"&gt;IO&lt;/span&gt; bandwidth
has. As a result, memory compression systems that (to some degree) prevent or
reduce disk &lt;span class="caps"&gt;IO&lt;/span&gt; are almost &lt;em&gt;always&lt;/em&gt; a&amp;nbsp;win.&lt;/p&gt;
&lt;p&gt;Anyway, because we definitely have a swap-file, we&amp;#8217;re going to use zswap here.
It&amp;#8217;s trivial to enable, but we&amp;#8217;re going to customize it a bit to get some more
out of it. To enable zswap you simply need to add the parameter
&lt;tt class="docutils literal"&gt;zswap.enabled=1&lt;/tt&gt; to your kernel command&amp;nbsp;line:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo sed -i -e 's/$/ zswap.enabled=1/' /boot/firmware/cmdline.txt
&lt;/pre&gt;
&lt;p&gt;This is sufficient to enable zswap; if you were to reboot at this point it
would already be working. If you don&amp;#8217;t want to get into things that are a bit
more complex, then feel free to stop at this point &amp;#8212; you&amp;#8217;ll already have
plenty of&amp;nbsp;benefit.&lt;/p&gt;
&lt;p&gt;Still here? Okay, zswap&amp;#8217;s defaults are, if anything, a bit conservative. The
default allocator (zbud) is limited to 2 compressed objects per page. A
superior allocator (z3fold) permits 3 objects per page boosting the typical
compression ratio. Furthermore, the default compression algorithm is &amp;#8220;lzo&amp;#8221;
which is nice and fast, but doesn&amp;#8217;t provide &lt;em&gt;great&lt;/em&gt; compression. Other options
include &amp;#8220;lz4&amp;#8221;, &amp;#8220;lz4hc&amp;#8221; (&lt;span class="caps"&gt;LZ4&lt;/span&gt; with &amp;#8220;high compression&amp;#8221;), and &amp;#8220;zstd&amp;#8221;. Personally,
I&amp;#8217;ve gone for &amp;#8220;lz4&amp;#8221;; &amp;#8220;zstd&amp;#8221; is also tempting but remember we&amp;#8217;re balancing
compression with speed here, and despite zstd being better at compressing
things, lz4 is &lt;em&gt;very&lt;/em&gt;&amp;nbsp;fast.&lt;/p&gt;
&lt;p&gt;Unfortunately, we can&amp;#8217;t &amp;#8220;just&amp;#8221; enable these as we enabled zswap above. We need
to make sure that the modules for these features are included in the initramfs
at boot&amp;nbsp;time:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo -i
# echo lz4 &amp;gt;&amp;gt; /etc/initramfs-tools/modules
# echo z3fold &amp;gt;&amp;gt; /etc/initramfs-tools/modules
# update-initramfs -u
&lt;/pre&gt;
&lt;p&gt;Then simply add some more parameters to your kernel command&amp;nbsp;line:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# sed -i -e 's/$/ zswap.compressor=lz4/' /boot/firmware/cmdline.txt
# sed -i -e 's/$/ zswap.zpool=z3fold/' /boot/firmware/cmdline.txt
&lt;/pre&gt;
&lt;p&gt;Now reboot and you should have your super-whizzy compressed &lt;span class="caps"&gt;RAM&lt;/span&gt; setup complete!
To check everything is set correctly (after rebooting) you can run the&amp;nbsp;following:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ grep -R . /sys/module/zswap/parameters
/sys/module/zswap/parameters/same_filled_pages_enabled:Y
/sys/module/zswap/parameters/enabled:Y
/sys/module/zswap/parameters/max_pool_percent:20
/sys/module/zswap/parameters/compressor:lz4
/sys/module/zswap/parameters/zpool:z3fold
/sys/module/zswap/parameters/accept_threshold_percent:90
&lt;/pre&gt;
&lt;p&gt;If things aren&amp;#8217;t right, have a look at &lt;tt class="docutils literal"&gt;/boot/firmware/cmdline.txt&lt;/tt&gt; in a text&amp;nbsp;editor.&lt;/p&gt;
&lt;p&gt;To see how zswap is&amp;nbsp;performing:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo grep -R . /sys/kernel/debug/zswap
/sys/kernel/debug/zswap/same_filled_pages:3418
/sys/kernel/debug/zswap/stored_pages:242039
/sys/kernel/debug/zswap/pool_total_size:353652736
/sys/kernel/debug/zswap/duplicate_entry:0
/sys/kernel/debug/zswap/written_back_pages:0
/sys/kernel/debug/zswap/reject_compress_poor:368
/sys/kernel/debug/zswap/reject_kmemcache_fail:0
/sys/kernel/debug/zswap/reject_alloc_fail:0
/sys/kernel/debug/zswap/reject_reclaim_fail:0
/sys/kernel/debug/zswap/pool_limit_hit:0
&lt;/pre&gt;
&lt;p&gt;I&amp;#8217;ve even tried this on a Pi 4 &lt;span class="caps"&gt;2GB&lt;/span&gt; model and, while it&amp;#8217;s not as nice as a Pi
400 (in particular due to the SoC running hotter), it makes it a pretty viable
desktop at least for light usage. It was perfectly capable of firing up Firefox
and playing an &lt;span class="caps"&gt;HD&lt;/span&gt; YouTube video, while also running LibreOffice Writer, and a
terminal with vim, all on a $35 &lt;span class="caps"&gt;2GB&lt;/span&gt;&amp;nbsp;Pi!&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;I should note we don&amp;#8217;t officially support using the desktop image on a &lt;span class="caps"&gt;2GB&lt;/span&gt;
Pi, but personally I think this little optimization makes it a reasonable
platform for the Ubuntu&amp;nbsp;desktop.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Hirsute Hippo&amp;#8217;s due for release on April 22nd, and the &lt;a class="reference external" href="https://discourse.ubuntu.com/t/hirsute-hippo-release-notes/19221"&gt;release notes&lt;/a&gt; are
definitely worth a quick&amp;nbsp;peek.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="desktop"></category><category term="hirsute"></category></entry><entry><title>A Week with the Pi Desktop</title><link href="https://waldorf.waveform.org.uk/2020/a-week-with-the-pi-desktop.html" rel="alternate"></link><published>2020-10-22T00:00:00+01:00</published><updated>2022-09-06T13:27:19+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-10-22:/2020/a-week-with-the-pi-desktop.html</id><summary type="html">&lt;p class="first last"&gt;The ups and downs of a week using the Pi as my only&amp;nbsp;desktop&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In a &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/installing-the-new-pi-ubuntu-desktop.html"&gt;previous post&lt;/a&gt; I mentioned I&amp;#8217;d be using a Pi as my exclusive desktop for
a week, mostly to validate that it actually worked (and that our guesstimate
that 4Gb &lt;span class="caps"&gt;RAM&lt;/span&gt; models were a reasonable &amp;#8220;base&amp;#8221;), but partly just for the&amp;nbsp;experience!&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a blow-by-blow account of how it went (there&amp;#8217;s a &lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt; at end if you
just want to scroll there&amp;nbsp;:)…&lt;/p&gt;
&lt;div class="section" id="monday"&gt;
&lt;h2&gt;Monday&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;d spent a few hours of the prior week setting up the system as I needed for
work so, come this morning, everything was pretty much as my desktop normally
is. Just on a&amp;nbsp;Pi.&lt;/p&gt;
&lt;img alt="The diminutive Raspberry Pi sat next to the empty hulk of my old PC" src="https://waldorf.waveform.org.uk/images/honey-i-shrunk-the-pc.jpg" /&gt;
&lt;p&gt;The Pi in question was a model 4B with 4Gb of &lt;span class="caps"&gt;RAM&lt;/span&gt;. It was booting off an
ancient 120Gb &lt;span class="caps"&gt;SSD&lt;/span&gt; I had lying around from an old &lt;span class="caps"&gt;PC&lt;/span&gt; (in that small black case
on the floor) and the eagle-eyed will note that there&amp;#8217;s absolutely no active
cooling or so much as a heat-sink installed on it in this picture. This is
deliberate: I was reasonably sure it was going to thermally throttle at some
point in the day, but I wanted to see how often that happened and how bad it
was in practice. In other words, trying to answer the question: is this usable
on an otherwise &amp;#8220;naked&amp;#8221;&amp;nbsp;Pi?&lt;/p&gt;
&lt;p&gt;Other than that, it was plugged into all the stuff I usually have plumbed into
my beefy (but aging, and now &lt;a class="reference external" href="https://twitter.com/waveform80/status/1312086341061050368"&gt;somewhat dead&lt;/a&gt;) Core&amp;nbsp;i7.&lt;/p&gt;
&lt;p&gt;First&amp;nbsp;tweaks:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Audio from rhythmbox was choppy. I&amp;#8217;d encountered this before and the hints
from the &amp;#8220;Audio&amp;#8221; section of my &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/ubuntu-desktops-on-the-pi.html"&gt;earlier notes&lt;/a&gt; on the Pi desktop soon sorted
this out. I&amp;#8217;ll see what we can do about fixing this in the final image but
given where it is I don&amp;#8217;t &lt;em&gt;think&lt;/em&gt; we can layer another file on top of it to
implement this just for the Pi. This will probably be something I just have
to stick in the release&amp;nbsp;notes.&lt;/li&gt;
&lt;li&gt;The wrong audio output device was always selected on boot (should be &amp;#8220;Analog
Output - Built-in Audio&amp;#8221;, but it was always &amp;#8220;Multichannel Output - Built-in
Audio&amp;#8221;). On a hunch, I tried disabling the &amp;#8220;module-switch-on-connect&amp;#8221; lines
in that same file (&lt;tt class="docutils literal"&gt;/etc/pulse/default.pa&lt;/tt&gt;) and the problem was solved,
although later I discovered this did mean that audio devices plugged into &lt;span class="caps"&gt;USB&lt;/span&gt;
weren&amp;#8217;t automatically selected. However, in my case, that&amp;#8217;s ideal as I didn&amp;#8217;t
&lt;em&gt;want&lt;/em&gt; them selected. Again, this may wind up being a release&amp;nbsp;note.&lt;/li&gt;
&lt;li&gt;No swap. Not an issue initially, but I figured I&amp;#8217;d be needing it sooner or
later so I set up a 1Gb swap file and some monitoring scripts to regularly
report things like system load, swap used, SoC temperature, etc. then added
them to my steadily growing tmux configuration. Will see what can be done
about fixing this for the&amp;nbsp;release.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first real test of the day: a meeting in Google Meet! I didn&amp;#8217;t have any
camera plumbed in at this point as I hadn&amp;#8217;t succeeded in getting the camera
module working properly with it yet, but I plugged in my nice &lt;span class="caps"&gt;USB&lt;/span&gt; microphone
(that&amp;#8217;s the &lt;span class="caps"&gt;USB&lt;/span&gt; audio device I mentioned above, which I don&amp;#8217;t want to use for
output, just input), fired up Chromium (installed from the snap-store) and
somewhat nervously&amp;nbsp;connected…&lt;/p&gt;
&lt;p&gt;My group were evidently feeling somewhat shy (!) and everyone had their camera
feed off, so it was just audio for this first challenge. Still, there were 5
people in the call so this was a reasonable first test. My audio apparently
worked perfectly, and I had no issues hearing anyone at my end. All pretty
smooth so&amp;nbsp;far!&lt;/p&gt;
&lt;p&gt;The Pi had been idling at ~60°C but that temperature slowly began to climb
during the call. Within about 10 minutes it was at the 78°C mark (the Pi 4
throttles at 80°C) and I was starting to get a bit nervous. It took quite a
while to get actually hit 80°C (maybe another 5 minutes) and at that point
there was an occasional drop-out from the audio when others were speaking but
otherwise the thermal throttling worked remarkably smoothly and everything
stayed connected and working! So far, so&amp;nbsp;good.&lt;/p&gt;
&lt;p&gt;After the call was over, I dove into some other work and wound up compiling a
couple of packages, writing up some notes and answering a few e-mails. The
compiling all went fine, no swapping, no throttling, but the e-mail… This
proved to be the next big challenge. Within minutes of firing up a GMail tab,
swap space was being eaten into. It really is quite the memory&amp;nbsp;hog!&lt;/p&gt;
&lt;p&gt;Next&amp;nbsp;tweaks:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;I activated &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Zswap"&gt;zswap&lt;/a&gt; (appended &lt;tt class="docutils literal"&gt;zswap.enabled=1&lt;/tt&gt; to
&lt;tt class="docutils literal"&gt;/boot/firmware/cmdline.txt&lt;/tt&gt;) to see if that alleviated the 50% of my 1Gb
swap space that&amp;#8217;d been eaten. It &lt;em&gt;seemed&lt;/em&gt; to (hovered around 20-30% after
loading everything back up after rebooting to check my config), but these
things are difficult to measure. &lt;span class="caps"&gt;YMMV&lt;/span&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, the next big challenge of the day. Another Google Meet call, but this time
one that usually had 10+ participants and was very likely to involve video
streams. I opted to try this one in Firefox instead (just to see if it would
work). Within a minute of connecting it was obvious the Pi was struggling; the
SoC temperature hit 80°C (the throttle point) within the first minute, and the
video streams were mostly frozen. That said, at all points audio from the call
was playing smoothly. With a fair bit of patience (very slow &lt;span class="caps"&gt;UI&lt;/span&gt;!), I disabled
the incoming video feeds so the Pi just had audio to deal&amp;nbsp;with.&lt;/p&gt;
&lt;p&gt;Then the next challenge: using a shared Google Doc in another tab with the call
still going. I was highly dubious the Pi would manage this given it was already
into thermal throttling and swapping but I opened the tab anyway. It wasn&amp;#8217;t
quick to load but to my amazement it worked! I could see others editing away
and could make a few edits myself. It wasn&amp;#8217;t smooth, it did have a fair bit of
cursor lag, but at all points the audio of the call kept playing mostly
smoothly (a few crackles here and there, but nothing to affect comprehension),
and I could actually work semi-productively. Remember, this was on the smallest
of the Pis supported, with no thermal management at all and no shutting down
anything I was already&amp;nbsp;running.&lt;/p&gt;
&lt;p&gt;Impressions at the end of the day: it&amp;#8217;s slightly uncanny. Several times during
the day I had to remind myself that I wasn&amp;#8217;t on Ammy (my old Core i7). To be
clear: I&amp;#8217;m not claiming they&amp;#8217;re in any way comparable on performance because
they&amp;#8217;re not. It&amp;#8217;s trivial to find something that shows a massive performance
difference (like Google Meet calls, or using GMail), but for most of the time,
playing some music in the background while I hacked away at something in vim
under a terminal window there was effectively no&amp;nbsp;difference.&lt;/p&gt;
&lt;p&gt;Then I&amp;#8217;d need to flash a fresh image onto an &lt;span class="caps"&gt;SD&lt;/span&gt; card to test something, and
suddenly I&amp;#8217;d find myself reaching down to stick the card in the flash-reader
that used to be in one of Ammy&amp;#8217;s 5.25&amp;#8221; bays and realize: nope, I&amp;#8217;m on the Pi,
and I need to use that nice little card-reader-slash-usb-hub I&amp;#8217;d stuck on the
desk&amp;nbsp;instead.&lt;/p&gt;
&lt;img alt="The neat little USB3 hub / card-reader / card writer on my desk" src="https://waldorf.waveform.org.uk/images/the-replacement-reader.jpg" /&gt;
&lt;p&gt;To my genuine surprise, there wasn&amp;#8217;t one thing the Pi &lt;em&gt;failed&lt;/em&gt; at all day. Bear
in mind this was an uncooled, more or less &amp;#8220;stock&amp;#8221; 4Gb Pi. Sure, the Google
Meet calls weren&amp;#8217;t great, and the GMail &lt;span class="caps"&gt;UI&lt;/span&gt; was hilariously laggy in some places
… but they worked! Nothing crashed, nothing hung, and I had a day about as
productive as any normal&amp;nbsp;day.&lt;/p&gt;
&lt;p&gt;If there&amp;#8217;s one major difference that I&amp;#8217;m just noticing it&amp;#8217;s that, as I write
this at 8 o&amp;#8217;clock in the evening, my fingers are&amp;nbsp;cold.&lt;/p&gt;
&lt;p&gt;This may not sound too surprising: my office is in an unheated loft extension,
it&amp;#8217;s mid-October, and I&amp;#8217;m in the North of the &lt;span class="caps"&gt;UK&lt;/span&gt;, so it&amp;#8217;s pretty chilly
outside. But normally Ammy, with her mammoth heat-sink, myriad fans, and
frankly silly graphics card, has done a pretty good job of keeping things
toasty and warm up here. She&amp;#8217;s usually more than capable of raising the
temperature in this room up to 30°C (in summer, things get ridiculously hot up
here!). But the Pi has nothing like that thermal output. I might have to get
some&amp;nbsp;gloves…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tuesday"&gt;
&lt;h2&gt;Tuesday&lt;/h2&gt;
&lt;p&gt;Played around a bit with the configuration today to test some extra&amp;nbsp;interfaces:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Unplugged my usual Microsoft Intellimouse and tried out a bluetooth-based
mouse instead. After some considerable pain getting it paired (which may be
because it was previously paired to another machine), it&amp;#8217;s working&amp;nbsp;happily.&lt;/li&gt;
&lt;li&gt;Paired the Pi with my mobile phone and tried out bluetooth-based tethering.
Totally uneventful: just&amp;nbsp;worked.&lt;/li&gt;
&lt;li&gt;Also fitted a &lt;a class="reference external" href="https://shop.pimoroni.com/products/fan-shim#show-accessories"&gt;Pimoroni fan-shim&lt;/a&gt; to see what difference that made.
Thankfully since this image doesn&amp;#8217;t have U-Boot there were no problems with
the fan-shim &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/u-boot/+bug/1873520"&gt;interrupting it&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To configure the temperature limit on the fan-shim, I simply added the
following line to &lt;tt class="docutils literal"&gt;/boot/firmware/config.txt&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
dtoverlay=gpio-fan,gpiopin=18,temp=65000
&lt;/pre&gt;
&lt;p&gt;The only thing that brought the Pi into throttling territory yesterday were the
calls in Google Meet, so I was interested to see what difference that made
today. For the first call of the day there were only a few people with cameras
on, but that was enough to quickly push the Pi&amp;#8217;s temperature&amp;nbsp;up.&lt;/p&gt;
&lt;p&gt;Sure enough, the fan-shim kept things nicely below 65°C throughout the call.
However, while this meant the audio was perfect throughout, I still wound up
having to switch off most of the incoming video streams to keep things
operating smoothly (Google Meet has a nice option to show a single standard-def
stream at a time which seemed to work well). On this call, I was still audio&amp;nbsp;only.&lt;/p&gt;
&lt;p&gt;Then it was back to some compiling and documentation work, no problems here and
everything went&amp;nbsp;smoothly.&lt;/p&gt;
&lt;p&gt;Next call: at this point while I&amp;#8217;d managed to get a camera module installed,
there was a problem in that the cable isn&amp;#8217;t that long and I didn&amp;#8217;t think people
would particularly enjoy a view from &lt;em&gt;under&lt;/em&gt; my desk (ahem). So, instead I
plugged in the &lt;span class="caps"&gt;USB&lt;/span&gt; web-cam that normally sits on my monitor. I knew this was
likely to give the Pi a hard time as &lt;span class="caps"&gt;USB&lt;/span&gt; web-cams typically chuck out an &lt;span class="caps"&gt;MJPEG&lt;/span&gt;
stream and it would be up to Chromium to convert that to &lt;span class="caps"&gt;VP8&lt;/span&gt; (or &lt;span class="caps"&gt;VP9&lt;/span&gt;?) which it
wouldn&amp;#8217;t use the &lt;span class="caps"&gt;GPU&lt;/span&gt; for. Sure enough, within a few seconds of connecting it
was obvious the Pi was struggling (apparently my audio was choppy and dropping
out), so I disabled my video feed and carried on as&amp;nbsp;normal.&lt;/p&gt;
&lt;p&gt;Finally: needed to modify some bits in &lt;span class="caps"&gt;JIRA&lt;/span&gt; (which we use for organizing the
team&amp;#8217;s work). &lt;span class="caps"&gt;JIRA&lt;/span&gt; was &amp;#8230; hilariously slow. Nearly a full minute to just load
the web-page. That said, &lt;span class="caps"&gt;JIRA&lt;/span&gt; is hilariously, painfully, slow on just about
anything I&amp;#8217;ve used (including Ammy) so I shouldn&amp;#8217;t be surprised there. It was
useable with a good dose of patience, but I&amp;#8217;m thankful it&amp;#8217;s not something I
spend all my time in (can&amp;#8217;t see my boss using it with a Pi&amp;nbsp;:).&lt;/p&gt;
&lt;p&gt;Verdict for the day: bluetooth was fine; the fan-shim is probably worthwhile
for heavy workloads; &lt;span class="caps"&gt;USB&lt;/span&gt; web-cams are not going to be happy on video calls -
use a camera module if at all possible (though cable length may be an&amp;nbsp;issue).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="wednesday"&gt;
&lt;h2&gt;Wednesday&lt;/h2&gt;
&lt;p&gt;Switched up to a Pi4 8Gb today. I was satisfied with the 4Gb&amp;#8217;s performance with
a decent dose of swap-file but was now interested in whether the 8Gb would swap
at all and whether the (hypothetical) lack of swap would lead to any serious
performance&amp;nbsp;difference.&lt;/p&gt;
&lt;img alt="The 8Gb Pi 4 with Pimoroni Fan-Shim installed" src="https://waldorf.waveform.org.uk/images/kermit.jpg" /&gt;
&lt;p&gt;I transferred the fan-shim to this Pi so I could compare &lt;em&gt;just&lt;/em&gt; the difference
in &lt;span class="caps"&gt;RAM&lt;/span&gt; rather than anything else. Everything else was just unplugged from the
old Pi and plugged into the new Pi. This is one of the things I love about the
Pi: transferring all your storage between them is trivial (whether &lt;span class="caps"&gt;SD&lt;/span&gt; card or
&lt;span class="caps"&gt;USB&lt;/span&gt; hard-drive based, it&amp;#8217;s a lot easier than messing around inside a &lt;span class="caps"&gt;PC&lt;/span&gt;&amp;nbsp;tower!).&lt;/p&gt;
&lt;p&gt;There was some small faff getting the &lt;span class="caps"&gt;EEPROM&lt;/span&gt; &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/installing-the-new-pi-ubuntu-desktop.html"&gt;configured to handle &lt;span class="caps"&gt;USB&lt;/span&gt; boot&lt;/a&gt;,
but I had an &lt;span class="caps"&gt;SD&lt;/span&gt; card already setup to deal with that so it didn&amp;#8217;t take more
than a minute or two, then we were&amp;nbsp;off.&lt;/p&gt;
&lt;p&gt;The day was frankly uneventful. The usual mix of meets, compiling, emailing,
etc. Only one thing of note really: no swapping. With a judicious set of &lt;span class="caps"&gt;JIRA&lt;/span&gt;,
Gmail, and other hogs open I did manage to push the Pi to ~70% &lt;span class="caps"&gt;RAM&lt;/span&gt; usage but
never managed to get it to actually swap (beyond trivial&amp;nbsp;levels).&lt;/p&gt;
&lt;p&gt;However, I can&amp;#8217;t claim I actually noticed any performance difference. It felt
pretty much exactly the same as the 4Gb model - the only difference being that
I could see the swap monitor sat resolutely at zero all&amp;nbsp;day.&lt;/p&gt;
&lt;p&gt;Verdict for the day: the 8Gb is certainly a technical improvement, and for some
that extra memory may be crucial (especially if you&amp;#8217;re heavily into
containers). Personally, I could live happily with a 4Gb plus some swap.
Incidentally, even on an 8Gb I&amp;#8217;d still keep some swap (say, 1Gb) around.
The myths surrounding things being faster without swap are generally just that:
&lt;a class="reference external" href="https://chrisdown.name/2018/01/02/in-defence-of-swap.html"&gt;myths&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="thursday"&gt;
&lt;h2&gt;Thursday&lt;/h2&gt;
&lt;p&gt;Still on the Pi4 8Gb today. I did cheat on my Google Meet calls today, and used
my little laptop instead, largely because I knew one of the calls was going to
involve screen-sharing and at this point I was pretty confident I knew the Pi&amp;#8217;s
limits and that I couldn&amp;#8217;t smoothly deal with that, and ~10 people on the call
too (on Google Meet, if you opt for &amp;#8220;Audio only&amp;#8221; it really means that and
doesn&amp;#8217;t permit screen-sharing; unsurprising but slightly annoying&amp;nbsp;today).&lt;/p&gt;
&lt;p&gt;Come the evening I had to &amp;#8220;cheat&amp;#8221; on another meeting call but for different
reasons. I was due to give a quick talk for the Python North West group, which
was using Zoom and, about 30 minutes before I was due to connect, discovered
there&amp;#8217;s no Zoom client for &lt;span class="caps"&gt;ARM&lt;/span&gt;. I wouldn&amp;#8217;t have minded going audio-only for
this one - I didn&amp;#8217;t have any slides, just a few notes for a quick 10 minute
segment. Still, it was back to the laptop&amp;nbsp;again.&lt;/p&gt;
&lt;p&gt;Everything else was basically&amp;nbsp;uneventful:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Needed to do some printing for my kid (colour laser printer on the network);
worked without a hitch as it usually did on&amp;nbsp;Ammy&lt;/li&gt;
&lt;li&gt;Needed to scan some pages for my partner (&lt;span class="caps"&gt;USB&lt;/span&gt; connected scanner); also worked
without a&amp;nbsp;hitch&lt;/li&gt;
&lt;li&gt;Did some quick image tweaks in Gimp (for this post!) with my partner&amp;#8217;s Wacom
tablet. No problems there either (just plug&amp;#8217;n&amp;#8217;play), though I&amp;#8217;m pretty sure
the extra &lt;span class="caps"&gt;RAM&lt;/span&gt; of the 8Gb model&amp;nbsp;helped&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Slight hassle plugging and unplugging all these &lt;span class="caps"&gt;USB&lt;/span&gt; things, but necessary. The
Pi&amp;#8217;s power supply isn&amp;#8217;t a 750W beast like Ammy&amp;#8217;s and doesn&amp;#8217;t like having &lt;em&gt;all&lt;/em&gt;
my &lt;span class="caps"&gt;USB&lt;/span&gt; gadgets plugged in at once. Further, given I&amp;#8217;m also &lt;em&gt;booting&lt;/em&gt; off a &lt;span class="caps"&gt;USB&lt;/span&gt;
attached device it&amp;#8217;d be ill-advised to try and starve that of power! Might be
time to invest in a powered &lt;span class="caps"&gt;USB&lt;/span&gt;&amp;nbsp;hub…&lt;/p&gt;
&lt;p&gt;Verdict for the day: if &lt;em&gt;only&lt;/em&gt; Chromium could use the &lt;span class="caps"&gt;GPU&lt;/span&gt; for video encode and
decode here. We&amp;#8217;ll have to see what can be done about that (this is easier said
than done; I have some inkling of what the Pi Foundation have gone through to
get &lt;span class="caps"&gt;GPU&lt;/span&gt; decode into their version of Chromium, and it&amp;#8217;s&amp;nbsp;non-trivial).&lt;/p&gt;
&lt;p&gt;Also, more stuff needs compiling for &lt;span class="caps"&gt;ARM&lt;/span&gt;. After the lack-of-Zoom-client
discovery I went checking around for a few things. There&amp;#8217;s no Chrome for &lt;span class="caps"&gt;ARM&lt;/span&gt;
(well, outside of Chromebooks, so it&amp;#8217;s obviously &lt;em&gt;possible&lt;/em&gt;), no Zoom (as noted
already), and no Skype. Signal and Telegram are available, but there&amp;#8217;s
definitely some prodding to be done&amp;nbsp;here…&lt;/p&gt;
&lt;p&gt;Side note: it was amusing to note the number of sites that happily offered me
an amd64&amp;nbsp;deb!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="friday"&gt;
&lt;h2&gt;Friday&lt;/h2&gt;
&lt;p&gt;Still on the Pi4 8Gb. Cheated on the Google Meet calls again with the laptop
because, well, I might as well at this point. Everything else still on the Pi4,
and uneventful. Watched a few YouTube videos; slightly jerky startup for the
first second, and then played entirely smoothly while I was still working in
another&amp;nbsp;window.&lt;/p&gt;
&lt;p&gt;Looks like it&amp;#8217;s happy dealing with 1 video stream (even with it just doing &lt;span class="caps"&gt;CPU&lt;/span&gt;
decoding), but pushing it to do more than that (e.g. during conferencing) isn&amp;#8217;t
viable without hardware&amp;nbsp;acceleration.&lt;/p&gt;
&lt;p&gt;Wound up doing some work in the evening on the piwheels website. The Firefox
and Chromium developer tools are certainly a bit sluggish, but still useable.
Once used to the slower refresh rate, it didn&amp;#8217;t slow me&amp;nbsp;down.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tl-dr-conclusion"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt; /&amp;nbsp;Conclusion&lt;/h2&gt;
&lt;img alt="A collection of windows on the Ubuntu 20.10 Desktop running on the pi" src="https://waldorf.waveform.org.uk/images/desktop-screenshot.png" /&gt;
&lt;p&gt;It&amp;#8217;s &lt;em&gt;so&lt;/em&gt; close to being a fully viable desktop replacement. The thing holding
it back for me is video acceleration on Google Meet calls. If we had that, I
could effectively switch to using this for the vast majority of my work, firing
up the big &lt;span class="caps"&gt;PC&lt;/span&gt; (when it&amp;#8217;s fixed :) only for seriously heavy duty things like
video editing, or &lt;span class="caps"&gt;PC&lt;/span&gt; gaming, which are pretty rare activities for me, and which
it really wouldn&amp;#8217;t be realistic to expect a Pi to&amp;nbsp;accomplish.&lt;/p&gt;
&lt;p&gt;For others? Very much depends on typical uses and software. If you need Skype
or Zoom, for instance, you&amp;#8217;ll be stuck using the web-based versions (which&amp;#8217;ll
have the same issues as I did with Google Meet) because there&amp;#8217;s no &lt;span class="caps"&gt;ARM&lt;/span&gt; compiled
native clients (yet). But if you don&amp;#8217;t need video conferencing (okay, that&amp;#8217;s
probably a fairly major ask in the midst of a pandemic where everyone in front
of a computer is probably home-working), it&amp;#8217;s a capable&amp;nbsp;replacement.&lt;/p&gt;
&lt;p&gt;Don&amp;#8217;t go expecting Ryzen-like performance because you&amp;#8217;re not going to get it …
obviously. But I&amp;#8217;d hardly call it sluggish either. Running off a &lt;span class="caps"&gt;USB&lt;/span&gt;-attached
&lt;span class="caps"&gt;SSD&lt;/span&gt; everything starts up quickly and runs pretty smoothly. I was also impressed
that, even on the 4Gb model, despite it eating into swap a bit, nothing ever
felt like it was overwhelming the system. Though you might want to invest in a
powered &lt;span class="caps"&gt;USB&lt;/span&gt;-hub!&lt;/p&gt;
&lt;p&gt;Finally, for all the talk of the Pi 4 &amp;#8220;running hot&amp;#8221;, I only ever &lt;em&gt;needed&lt;/em&gt; the
fan-shim for video conferencing. My other workloads failed to push it into
thermal throttling (though a few got close). The board certainly gets toasty,
but it&amp;#8217;s nothing like the small fan heater that Ammy was. If only it could heat
my fingers up a&amp;nbsp;bit!&lt;/p&gt;
&lt;p&gt;Final note: any issues we haven&amp;#8217;t sorted out by release, with any known
workarounds, should be mentioned in the &lt;a class="reference external" href="https://discourse.ubuntu.com/t/groovy-gorilla-release-notes/15533"&gt;Groovy Release Notes&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="desktop"></category><category term="groovy"></category></entry><entry><title>Groovy Boot Modes</title><link href="https://waldorf.waveform.org.uk/2020/groovy-boot-modes.html" rel="alternate"></link><published>2020-10-21T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-10-21:/2020/groovy-boot-modes.html</id><summary type="html">&lt;p class="first last"&gt;Adding or removing U-Boot in Ubuntu&amp;nbsp;Groovy&lt;/p&gt;
</summary><content type="html">&lt;p&gt;By default, Ubuntu Groovy Pi images (both Server and Desktop) will ship with a
boot sequence that involves the Pi&amp;#8217;s native bootloader &lt;em&gt;only&lt;/em&gt;. In other words,
the new boot sequence is&amp;nbsp;simply:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
Pi Bootloader --&amp;gt; Linux Kernel --&amp;gt; Initrd
&lt;/pre&gt;
&lt;p&gt;Previous versions of Ubuntu on the Pi have included U-Boot in the sequence.
In other words, in Focal (and prior) versions, the boot sequence&amp;nbsp;was:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
Pi Bootloader --&amp;gt; U-Boot --&amp;gt; Linux Kernel --&amp;gt; Initrd
&lt;/pre&gt;
&lt;p&gt;Users &lt;em&gt;upgrading&lt;/em&gt; to Groovy will continue using the legacy U-Boot sequence but
are free to switch to the new &amp;#8220;native only&amp;#8221; boot sequence by following the
instructions below. Conversely, new users of Groovy may also switch to a U-Boot
based sequence if they so wish (the U-Boot binaries will still be installed on
the boot partition even when they&amp;#8217;re not used to enable switching between the
two&amp;nbsp;choices).&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Be aware that the U-Boot based sequence does &lt;em&gt;not&lt;/em&gt; support native &lt;span class="caps"&gt;USB&lt;/span&gt; boot,
or the Pi&amp;#8217;s native netboot (U-Boot on the Pi 3 and earlier does have its
own &lt;span class="caps"&gt;PXE&lt;/span&gt;-like netboot system, however our build of U-Boot currently lacks
ethernet drivers for the Pi&amp;nbsp;4).&lt;/p&gt;
&lt;p class="last"&gt;Also note that the U-Boot based sequence is now deprecated. If you are
still relying on U-Boot for some reason, please let me know what it is
(open a ticket against &lt;a class="reference external" href="https://launchpad.net/ubuntu/+source/u-boot"&gt;u-boot on Launchpad&lt;/a&gt;, leave a comment below, or
pester me on &lt;a class="reference external" href="https://twitter.com/waveform80/"&gt;Twitter&lt;/a&gt;)!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="switching-from-u-boot-to-native"&gt;
&lt;h2&gt;Switching from U-Boot to&amp;nbsp;Native&lt;/h2&gt;
&lt;p&gt;The only file that needs editing is &lt;tt class="docutils literal"&gt;/boot/firmware/config.txt&lt;/tt&gt;. There
may be warnings at the top of the warning you not to edit it. You can ignore
these. I know this, because I wrote them (you can read the sordid tale of those
warnings and why they&amp;#8217;re now redundant in &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/boot-configuration-with-pibootctl.html"&gt;this post&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Remove any existing lines that start with &lt;tt class="docutils literal"&gt;kernel=&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;initramfs&lt;/tt&gt;, or
&lt;tt class="docutils literal"&gt;devicetree_address=&lt;/tt&gt; from this file. The add the following lines under the
&lt;code class="ini"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;&lt;/code&gt; section:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
kernel=vmlinuz
initramfs initrd.img followkernel
&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s all there is to it! If you want to try this from &lt;a class="reference external" href="https://pibootctl.readthedocs.io/"&gt;pibootctl&lt;/a&gt; (with some
extra lines to save your current boot configuration so you can switch back to
it if you wish), the following commands should do the&amp;nbsp;trick:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;save&lt;span class="w"&gt; &lt;/span&gt;uboot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;boot.kernel.filename&lt;span class="o"&gt;=&lt;/span&gt;vmlinuz&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;boot.initramfs.filename&lt;span class="o"&gt;=&lt;/span&gt;initrd.img&lt;span class="w"&gt; &lt;/span&gt;boot.devicetree.address&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;save&lt;span class="w"&gt; &lt;/span&gt;native
&lt;/pre&gt;
&lt;p&gt;Now you can simply do the following to switch back to uboot if you&amp;nbsp;wish:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;uboot
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="switching-from-native-to-u-boot"&gt;
&lt;h2&gt;Switching from Native to&amp;nbsp;U-Boot&lt;/h2&gt;
&lt;p&gt;Switching back to U-Boot from the native boot sequence is a little more complex
but not much so. Again, in &lt;tt class="docutils literal"&gt;/boot/firmware/config.txt&lt;/tt&gt; remove all lines
that start with &lt;tt class="docutils literal"&gt;kernel=&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;initramfs&lt;/tt&gt;. Next, determine whether you&amp;#8217;re
using a 32-bit or&amp;nbsp;64-bit:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;dpkg&lt;span class="w"&gt; &lt;/span&gt;--print-architecture&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;arm64&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;If you&amp;#8217;re on arm64 (64-bit), you want a &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; that includes the
following (by &amp;#8220;includes&amp;#8221; I mean don&amp;#8217;t replace your whole configuration with
this; either append this to your current configuration or merge it into the
existing sections - either should be&amp;nbsp;fine):&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="k"&gt;[pi4]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_4.bin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;[pi2]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_2.bin&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;[pi3]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_3.bin&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;device_tree_address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0x03000000&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;If you&amp;#8217;re on armhf (32-bit), you want a &lt;tt class="docutils literal"&gt;config.txt&lt;/tt&gt; that includes the&amp;nbsp;following:&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="k"&gt;[pi4]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_4_32b.bin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;[pi2]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_2.bin&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;[pi3]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_3_32b.bin&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;device_tree_address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0x03000000&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;(if you&amp;#8217;re curious about the strange ordering of the sections, this isn&amp;#8217;t
strictly required on Groovy but is just there for historical&amp;nbsp;reasons)&lt;/p&gt;
&lt;p&gt;You can try this doing this via &lt;a class="reference external" href="https://pibootctl.readthedocs.io/"&gt;pibootctl&lt;/a&gt;, but it currently only has options
for modifying boot configuration sections relevant to the machine you&amp;#8217;re
running on (e.g. if you&amp;#8217;re on a Pi 4, it can modify the &lt;code class="ini"&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;&lt;/code&gt; and
&lt;code class="ini"&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;&lt;/code&gt; sections but won&amp;#8217;t modify &lt;code class="ini"&gt;&lt;span class="k"&gt;[pi2]&lt;/span&gt;&lt;/code&gt; or &lt;code class="ini"&gt;&lt;span class="k"&gt;[pi3]&lt;/span&gt;&lt;/code&gt;
sections). So, if you&amp;#8217;re on a Pi 4 running an arm64 kernel, you could do the
following (with some extra lines to save your current boot configuration so you
can switch back to it if you&amp;nbsp;wish):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;save&lt;span class="w"&gt; &lt;/span&gt;native&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--this-model&lt;span class="w"&gt; &lt;/span&gt;boot.kernel.filename&lt;span class="o"&gt;=&lt;/span&gt;uboot_rpi_4.bin&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--no-backup&lt;span class="w"&gt; &lt;/span&gt;boot.initramfs.filename&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;boot.devicetree.address&lt;span class="o"&gt;=&lt;/span&gt;0x3000000&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;save&lt;span class="w"&gt; &lt;/span&gt;uboot
&lt;/pre&gt;
&lt;p&gt;Now you can simply do the following to switch back to uboot if you&amp;nbsp;wish:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;native
&lt;/pre&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="boot"></category><category term="pibootctl"></category></entry><entry><title>Installing the new Pi Ubuntu Desktop</title><link href="https://waldorf.waveform.org.uk/2020/installing-the-new-pi-ubuntu-desktop.html" rel="alternate"></link><published>2020-10-12T00:00:00+01:00</published><updated>2021-04-15T23:15:01+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-10-12:/2020/installing-the-new-pi-ubuntu-desktop.html</id><summary type="html">&lt;p class="first last"&gt;Installing the official Ubuntu desktop for the Raspberry&amp;nbsp;Pi&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Now that Martin&amp;#8217;s &lt;a class="reference external" href="https://twitter.com/m_wimpress/status/1314315931468914689"&gt;given the game away&lt;/a&gt; on Twitter, it&amp;#8217;s probably a good idea
for me to post a quick guide on installing the new Ubuntu Desktop for the Pi on
various media. So, here&amp;nbsp;goes!&lt;/p&gt;
&lt;div class="section" id="stuff-you-ll-need"&gt;
&lt;h2&gt;Stuff you&amp;#8217;ll&amp;nbsp;need&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;A Raspberry Pi Model 4B with 4Gb of &lt;span class="caps"&gt;RAM&lt;/span&gt; or higher. Theoretically, the desktop
can run on less but we&amp;#8217;re only officially supporting it on these models
because with less than 4Gb of &lt;span class="caps"&gt;RAM&lt;/span&gt; it&amp;#8217;s not a particularly &amp;#8220;useful&amp;#8221;&amp;nbsp;experience!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The &lt;a class="reference external" href="https://www.raspberrypi.org/downloads/"&gt;Raspberry Pi Imager&lt;/a&gt; tool for whatever platform you&amp;#8217;re&amp;nbsp;on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The Ubuntu Desktop for Raspberry Pi image, for now anyway. Once Groovy&amp;#8217;s
released it&amp;#8217;ll just appear in the menu in the imager tool, but for now you
can grab the pre-release beta&amp;nbsp;from:&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://cdimage.ubuntu.com/ubuntu/daily-preinstalled/current/"&gt;http://cdimage.ubuntu.com/ubuntu/daily-preinstalled/current/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You need the &lt;a class="reference external" href="http://cdimage.ubuntu.com/ubuntu/daily-preinstalled/current/groovy-preinstalled-desktop-arm64+raspi.img.xz"&gt;groovy-preinstalled-desktop-arm64+raspi.img.xz&lt;/a&gt; image from
there. You may note there&amp;#8217;s no armhf image and the simple reason for that is
we&amp;#8217;re not providing one at this time; it&amp;#8217;s arm64&amp;nbsp;only.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, continue with one of the sections&amp;nbsp;below&amp;#8230;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="sd-card"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;Card&lt;/h2&gt;
&lt;p&gt;To install on an &lt;span class="caps"&gt;SD&lt;/span&gt; card is pretty much simplicity&amp;nbsp;itself:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Start the Imager&amp;nbsp;Tool&lt;/li&gt;
&lt;li&gt;Click &amp;#8220;Choose &lt;span class="caps"&gt;OS&lt;/span&gt;&amp;#8221;, select &amp;#8220;Use custom&amp;#8221;, and select the file you downloaded
earlier; you don&amp;#8217;t need to decompress it (after release you&amp;#8217;ll be able to
pick an entry under&amp;nbsp;&amp;#8220;Ubuntu&amp;#8221;)&lt;/li&gt;
&lt;li&gt;Click &amp;#8220;Choose &lt;span class="caps"&gt;SD&lt;/span&gt; Card&amp;#8221;, select the card you want to&amp;nbsp;flash&lt;/li&gt;
&lt;li&gt;Click&amp;nbsp;&amp;#8220;Write&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt="A preview of the Raspberry Pi Imager utility flashing a card" src="https://waldorf.waveform.org.uk/images/imager4.png" /&gt;
&lt;p&gt;Then you can boot the card on your Pi&amp;nbsp;4.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="usb-hdd-ssd"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;USB&lt;/span&gt; &lt;span class="caps"&gt;HDD&lt;/span&gt; / &lt;span class="caps"&gt;SSD&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;In Groovy we&amp;#8217;re (finally!) adding support for booting Ubuntu directly from a
&lt;span class="caps"&gt;USB&lt;/span&gt; attached hard-drive or &lt;span class="caps"&gt;SSD&lt;/span&gt; with no &lt;span class="caps"&gt;SSD&lt;/span&gt; card involved. The first thing to
ensure is that you&amp;#8217;ve got an up to date &lt;span class="caps"&gt;EEPROM&lt;/span&gt; version on your Pi 4 (as &lt;span class="caps"&gt;USB&lt;/span&gt;
booting was added after the Pi 4&amp;#8217;s release). This is easy enough to accomplish
under either Raspbian or&amp;nbsp;Ubuntu:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sudo apt install &lt;span class="pre"&gt;rpi-eeprom&lt;/span&gt;&lt;/tt&gt; - only strictly necessary on Ubuntu as it&amp;#8217;s
installed by default on Raspbian, but it won&amp;#8217;t do any harm&amp;nbsp;there&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sudo vcgencmd bootloader_config &amp;gt; bootconf.txt&lt;/tt&gt; - extract the current
bootloader configuration to a text&amp;nbsp;file&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sed &lt;span class="pre"&gt;-i&lt;/span&gt; &lt;span class="pre"&gt;-e&lt;/span&gt; &lt;span class="pre"&gt;'/^BOOT_ORDER=/&lt;/span&gt; &lt;span class="pre"&gt;s/=.*$/=0xf41/'&lt;/span&gt; bootconf.txt&lt;/tt&gt; - set the
BOOT_ORDER option to 0xf41 (meaning attempt &lt;span class="caps"&gt;SD&lt;/span&gt; card, then &lt;span class="caps"&gt;USB&lt;/span&gt; mass-storage
device, then repeat; see &lt;a class="reference external" href="https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711_bootloader_config.md"&gt;pi4 bootloader configuration&lt;/a&gt; for more
information). Alternatively &lt;tt class="docutils literal"&gt;vim bootconf.txt&lt;/tt&gt; and make the edits yourself
if you don&amp;#8217;t trust my sed-hacking&amp;nbsp;:)&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;rpi-eeprom-config&lt;/span&gt; &lt;span class="pre"&gt;--out&lt;/span&gt; &lt;span class="pre"&gt;pieeprom-new.bin&lt;/span&gt; &lt;span class="pre"&gt;--config&lt;/span&gt; bootconf.txt &lt;span class="pre"&gt;/lib/firmware/raspberrypi/bootloader/critical/pieeprom-2020-09-03.bin&lt;/span&gt;&lt;/tt&gt; -
generate a copy of the &lt;span class="caps"&gt;EEPROM&lt;/span&gt; with the updated&amp;nbsp;configuration&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sudo &lt;span class="pre"&gt;rpi-eeprom-update&lt;/span&gt; &lt;span class="pre"&gt;-d&lt;/span&gt; &lt;span class="pre"&gt;-f&lt;/span&gt; &lt;span class="pre"&gt;./pieeprom-new.bin&lt;/span&gt;&lt;/tt&gt; - set the system to flash
the new &lt;span class="caps"&gt;EEPROM&lt;/span&gt; firmware on the next&amp;nbsp;boot&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sudo reboot&lt;/tt&gt; - to apply any changes (the &lt;span class="caps"&gt;EEPROM&lt;/span&gt; is only updated during the
early stages of&amp;nbsp;boot)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Don&amp;#8217;t worry about having to do this on every &lt;span class="caps"&gt;EEPROM&lt;/span&gt; upgrade because you
don&amp;#8217;t have to. Each &lt;span class="caps"&gt;EEPROM&lt;/span&gt; upgrade extracts the current &lt;span class="caps"&gt;EEPROM&lt;/span&gt;
configuration and merges it with the updated &lt;span class="caps"&gt;EEPROM&lt;/span&gt; binary before flashing&amp;nbsp;it.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now that the &lt;span class="caps"&gt;EEPROM&lt;/span&gt;&amp;#8217;s dealt with, how do we get the image onto a hard&amp;nbsp;drive?&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Read the &lt;span class="caps"&gt;SD&lt;/span&gt; Card instructions&amp;nbsp;above&lt;/li&gt;
&lt;li&gt;Pretend your &lt;span class="caps"&gt;USB&lt;/span&gt;-attached hard-drive is a very fat &lt;span class="caps"&gt;SD&lt;/span&gt; card and just select it
instead when you click on &amp;#8220;Choose &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;Card&amp;#8221;&lt;/li&gt;
&lt;li&gt;erm &amp;#8230; that&amp;#8217;s&amp;nbsp;it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should now be able to boot your flashed drive on your Pi 4 off your
&lt;span class="caps"&gt;USB&lt;/span&gt;-attached hard-drive without any &lt;span class="caps"&gt;SD&lt;/span&gt; card inserted. Be aware that some drives
have issues being used to boot the Pi. In&amp;nbsp;particular:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Spinning hard-disks required a lot more power than SSDs and will very likely
required a &lt;em&gt;powered&lt;/em&gt; &lt;span class="caps"&gt;USB&lt;/span&gt; hub. If you have a stonkingly huge hard-drive, see
the next section&amp;nbsp;too&amp;#8230;&lt;/li&gt;
&lt;li&gt;Hubs themselves can cause compatibility issues, so frankly you&amp;#8217;re much better
off with an &lt;span class="caps"&gt;SSD&lt;/span&gt; to boot off (typically no need for a hub and no spin-up time&amp;nbsp;issues).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;#8217;s lots of good information on both the Pi forums and various GitHub
issues for debugging boot issues; here&amp;#8217;s a selection of links in a rough &amp;#8220;look
at this first&amp;#8221;&amp;nbsp;order:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.raspberrypi.org/forums/viewtopic.php?t=58151"&gt;Pi Forums: Is your Pi not&amp;nbsp;booting?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.raspberrypi.org/forums/viewtopic.php?f=63&amp;amp;t=277007"&gt;Pi Forums: &lt;span class="caps"&gt;USB&lt;/span&gt; &lt;span class="caps"&gt;MSD&lt;/span&gt; boot &lt;span class="caps"&gt;EEPROM&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.raspberrypi.org/forums/viewtopic.php?t=245931"&gt;Pi Forums: Pi 4 &lt;span class="caps"&gt;USB3&lt;/span&gt; &lt;span class="caps"&gt;SSD&lt;/span&gt; slow&amp;nbsp;speeds?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711_bootloader_config.md"&gt;Pi Docs: Pi 4 Bootloader&amp;nbsp;Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/raspberrypi/rpi-eeprom/issues/180"&gt;GitHub Issue: Enclosure doesn&amp;#8217;t power on after&amp;nbsp;reboot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="stonkingly-huge-hdd-ssd"&gt;
&lt;h2&gt;Stonkingly huge &lt;span class="caps"&gt;HDD&lt;/span&gt; / &lt;span class="caps"&gt;SSD&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;What if you have a hard-drive larger than 2Tb (or &lt;span class="caps"&gt;SSD&lt;/span&gt; if you&amp;#8217;re richer than
Croesus) and want to actually use all that space? The default &lt;span class="caps"&gt;MBR&lt;/span&gt; partitioning
on the image won&amp;#8217;t be much use as it can&amp;#8217;t address anything beyond 2Tb.
Thankfully, the Pi&amp;#8217;s bootloader now supports &lt;span class="caps"&gt;GPT&lt;/span&gt; partitioning too so this isn&amp;#8217;t
a big deal and just requires a minor tweak to the partition&amp;nbsp;table.&lt;/p&gt;
&lt;p&gt;The following instructions are Linux-specific. I&amp;#8217;m sure it&amp;#8217;s possible to do
this on Windows and Mac &lt;span class="caps"&gt;OS&lt;/span&gt; too, but you&amp;#8217;ll have to Google that if you need it.
I would advise doing this with the drive attached to another machine (i.e. when
you&amp;#8217;re not booted from it). It &lt;em&gt;might&lt;/em&gt; be possible to do this on a &amp;#8220;live&amp;#8221;
system, but I haven&amp;#8217;t tried that so &lt;span class="caps"&gt;YMMV&lt;/span&gt;!&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sudo apt install gdisk&lt;/tt&gt; - just to make sure you&amp;#8217;ve got gdisk (the &lt;span class="caps"&gt;GPT&lt;/span&gt;
partitioning tool)&amp;nbsp;installed&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;sudo gdisk /dev/sdX&lt;/tt&gt; - where sdX is whatever your hard-drive is called
(consult the end of &lt;tt class="docutils literal"&gt;dmesg&lt;/tt&gt; for a&amp;nbsp;hint)&lt;/li&gt;
&lt;li&gt;If gdisk complains about a valid &lt;span class="caps"&gt;MBR&lt;/span&gt; and a corrupt &lt;span class="caps"&gt;GPT&lt;/span&gt;, don&amp;#8217;t worry (this can
happen when re-imaging drives that&amp;#8217;ve had a &lt;span class="caps"&gt;GPT&lt;/span&gt; partition table before). Just
select &lt;tt class="docutils literal"&gt;1&lt;/tt&gt; to use the valid &lt;span class="caps"&gt;MBR&lt;/span&gt; and&amp;nbsp;continue&lt;/li&gt;
&lt;li&gt;gdisk should&amp;#8217;ve converted your &lt;span class="caps"&gt;MBR&lt;/span&gt; partition table to &lt;span class="caps"&gt;GPT&lt;/span&gt; in &lt;span class="caps"&gt;RAM&lt;/span&gt; (not written
to the disk yet). Enter &lt;tt class="docutils literal"&gt;p&lt;/tt&gt; to print the table and make sure it looks
relatively sane:&lt;ul&gt;
&lt;li&gt;two&amp;nbsp;partitions,&lt;/li&gt;
&lt;li&gt;a small one with type 0700 (&amp;#8220;Microsoft basic data&amp;#8221;) which is the boot
partition,&amp;nbsp;and&lt;/li&gt;
&lt;li&gt;a larger one with type 8300 (&amp;#8220;Linux filesystem&amp;#8221;) which is the root&amp;nbsp;partition&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Enter &lt;tt class="docutils literal"&gt;w&lt;/tt&gt; to write the new partition table to the disk and&amp;nbsp;exit.&lt;/li&gt;
&lt;li&gt;Safely eject the drive and boot from&amp;nbsp;it!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that you can do this before the first boot, or you can do it later on.
However, if you do this &lt;em&gt;after&lt;/em&gt; the first boot you may need to manually resize
the root partition to take up the full disk size (if that&amp;#8217;s what you want). If
you do this before the first boot, the image &lt;em&gt;should&lt;/em&gt; expand the root partition
to fill the disk (I&amp;#8217;ve checked resizing works under &lt;span class="caps"&gt;GPT&lt;/span&gt;, but I haven&amp;#8217;t got a
spare &lt;span class="caps"&gt;HD&lt;/span&gt; &amp;gt;2Tb to actually try that with!&amp;nbsp;:).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="have-you-actually-tested-this"&gt;
&lt;h2&gt;Have you actually tested&amp;nbsp;this?&lt;/h2&gt;
&lt;p&gt;For the last week I&amp;#8217;ve been using the desktop image on and off on a Pi 4 with
4Gb of &lt;span class="caps"&gt;RAM&lt;/span&gt; (I&amp;#8217;ve got an 8Gb model here, but obviously the point is to try and
validate that 4Gb is &amp;#8220;sufficient&amp;#8221;), with my little laptop as a backup. However,
this week I&amp;#8217;m embarking on something a little more adventurous: using it full
time as my only desktop (laptop strictly for emergency backup only, and no
desktop either because … well, it sort of &lt;a class="reference external" href="https://twitter.com/waveform80/status/1312086341061050368"&gt;blew up&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The specific configuration I&amp;#8217;m using is: booting off a small 120Gb &lt;span class="caps"&gt;SSD&lt;/span&gt; I had
spare. Anyway, if I come up with any tweaks along the way I&amp;#8217;ll post a write-up
later this week. Wish me&amp;nbsp;luck!&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="desktop"></category><category term="install"></category></entry><entry><title>Boot configuration with pibootctl</title><link href="https://waldorf.waveform.org.uk/2020/boot-configuration-with-pibootctl.html" rel="alternate"></link><published>2020-09-16T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-09-16:/2020/boot-configuration-with-pibootctl.html</id><summary type="html">&lt;p class="first last"&gt;Changing stuff with&amp;nbsp;pibootctl&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/using-pibootctl.html"&gt;previous post&lt;/a&gt;, we looked at reading the boot status with pibootctl
(in several formats). This time we&amp;#8217;ll look at how we can use pibootctl to
change the boot configuration, and the side-effects of doing&amp;nbsp;so.&lt;/p&gt;
&lt;p&gt;First though, a brief interlude on why this post took a lot longer than
intended (feel free to skip this section if you&amp;#8217;re only interested in how to
set stuff with pibootctl; it&amp;#8217;s mostly self-flagellation anyway!&amp;nbsp;:).&lt;/p&gt;
&lt;div class="section" id="on-what-dave-should-ve-known-sooner"&gt;
&lt;h2&gt;On What Dave Should&amp;#8217;ve Known&amp;nbsp;Sooner&lt;/h2&gt;
&lt;p&gt;When it started out, I had a notion of pibootctl&amp;#8217;s manipulation logic that was
based on certain assumptions, which in turn were based on how I&amp;#8217;d structured
the boot configuration for&amp;nbsp;Ubuntu.&lt;/p&gt;
&lt;p&gt;On Raspbian (oops, that&amp;#8217;s now Raspberry Pi &lt;span class="caps"&gt;OS&lt;/span&gt; - funny how so much can change in
a few months), the configuration structure is simple: everything goes in&amp;nbsp;&amp;#8220;config.txt&amp;#8221;.&lt;/p&gt;
&lt;p&gt;On Ubuntu, I&amp;#8217;d altered the configuration structure to be rather more complex:
everything starts in &amp;#8220;config.txt&amp;#8221; (as usual), then continues in the included
&amp;#8220;syscfg.txt&amp;#8221;, and finishes in the included &amp;#8220;usercfg.txt&amp;#8221;. The intention was as&amp;nbsp;follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;config.txt&amp;#8221; was to be under the control of the package manager. Its one and
only job would be to get the Linux kernel going and then include the other
files. The idea was we could clobber that from the package manager with
impunity, not worrying about arbitrary modifications to&amp;nbsp;it.&lt;/li&gt;
&lt;li&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;syscfg.txt&amp;#8221; was to be under the control of pibootctl (and thus anything
built on top of it). The pibootctl utility would be free to rewrite the whole
file according to whatever the user&amp;nbsp;requested.&lt;/li&gt;
&lt;li&gt;Finally &amp;#8220;usercfg.txt&amp;#8221; was the &amp;#8220;free for all&amp;#8221; file in which we migrated
existing settings from config.txt in the transition to the split. Users were
free to do whatever they wanted in this file, with the understanding &amp;#8220;you
break it, you get to keep the&amp;nbsp;pieces&amp;#8221;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Well, that&amp;#8217;s all fine and dandy but for one little problem: some boot
configuration settings only work in &amp;#8220;config.txt&amp;#8221; and get ignored if they&amp;#8217;re in
included files. In my defense, while this is now documented in the excellent
&lt;a class="reference external" href="https://www.raspberrypi.org/documentation/configuration/config-txt/misc.md"&gt;configuration documentation&lt;/a&gt; it wasn&amp;#8217;t &lt;a class="reference external" href="https://github.com/raspberrypi/documentation/commit/b385eec9911ec0aab697c23997406d50ddca0625"&gt;at the time&lt;/a&gt; I started writing it
(yes, I tracked down that commit after wondering &amp;#8220;how the hell did I miss&amp;nbsp;this?!&amp;#8221;).&lt;/p&gt;
&lt;p&gt;However, the case for the prosecution is: most of the directives affected (e.g.
start_x, start_debug, gpu_mem) are pretty obvious if you understand the
separation between &amp;#8220;bootcode.bin&amp;#8221; (or the &lt;span class="caps"&gt;EEPROM&lt;/span&gt; on the Pi 4), and &amp;#8220;start*.elf&amp;#8221;
… which I absolutely did. What I&amp;#8217;d missed was that, while many of the
conditional sections are supported by both &amp;#8220;bootcode.bin&amp;#8221; and &amp;#8220;start*.elf&amp;#8221;,
only the latter implements the &amp;#8220;include&amp;#8221;&amp;nbsp;command.&lt;/p&gt;
&lt;p&gt;Anyway, this meant that my oh-so-carefully separated boot config was a bust.
The &amp;#8220;config.txt&amp;#8221; file &lt;em&gt;must&lt;/em&gt; be modifiable by pibootctl (or anything else),
otherwise certain rather important settings (e.g. enabling the camera module)
just aren&amp;#8217;t possible. This in turn defied several of the assumptions baked into
pibootctl&amp;#8217;s manipulation of the boot&amp;nbsp;configuration:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;I had assumed that pibootctl could re-write a file (e.g. syscfg.txt)
completely and thus shouldn&amp;#8217;t need to concern itself with, for example,
preserving commentary in that file. This is obviously not the case if it
needs to manipulate &amp;#8220;config.txt&amp;#8221;&amp;nbsp;too.&lt;/li&gt;
&lt;li&gt;I had assumed there needn&amp;#8217;t be any provision for commenting / uncommenting
lines of configuration. We could just wipe that file and start fresh.
However, in &amp;#8220;config.txt&amp;#8221; there are traditionally comments preceding various
lines (esp. on Raspbian) and those comments should remain with their
corresponding configuration lines (á la &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;I had assumed that the included file we&amp;#8217;d be re-writing could reasonably be
assumed to be in an &amp;#8220;[all]&amp;#8221; context so we wouldn&amp;#8217;t need to worry about the
context of removed / commented lines, or the context of added lines. But, if
you&amp;#8217;re messing with &amp;#8220;config.txt&amp;#8221; that also goes out of the window. Both
Raspbian and Ubuntu use conditional sections in both of their default&amp;nbsp;configurations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All the assumptions above were now broken and needed to be worked around. The
tool needed to understand the conditional context applicable to each line, the
conditional context applicable to newly generated lines, and the set-based
relationship between conditional contexts. For example, given the following&amp;nbsp;configuration:&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="k"&gt;[pi2]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_2.bin&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;[pi3]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_3.bin&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uboot_rpi_4.bin&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;If I want to set the kernel&amp;#8217;s filename to &amp;#8220;vmlinuz&amp;#8221; (bypassing u-boot), on all
models, the tool needs to know that the conditional context of each &amp;#8220;kernel&amp;#8221;
line above is a subset of the requested conditional context (&amp;#8220;[all]&amp;#8221;), and thus
that all the existing &amp;#8220;kernel&amp;#8221; lines should be removed / commented; that a new
&amp;#8220;[all]&amp;#8221; section needs to be added at the end and the new line placed in that&amp;nbsp;section.&lt;/p&gt;
&lt;p&gt;Alternatively, if we want to make the same manipulation, but only for the
current model (let&amp;#8217;s assume that&amp;#8217;s a Pi 3), the tool needs to know that the
&amp;#8220;[pi2]&amp;#8221; section is a disjoint set with &amp;#8220;[pi3]&amp;#8221; so the first kernel line needs
to be left alone; the second is equal so that kernel line can be removed /
commented then replaced, and the final &amp;#8220;[pi4]&amp;#8221; section is also a disjoint set
with the requested&amp;nbsp;context.&lt;/p&gt;
&lt;p&gt;Anyway to cut an already way too long interlude short … that&amp;#8217;s what the tool
now does. It&amp;#8217;s fully aware of the conditional sections, how they relate, which
commands are only recognized in &amp;#8220;config.txt&amp;#8221; (and therefore should be ignored
for effect in other contexts), how to generate new conditional sections to
transform one conditional context into another, how to comment out lines
(optionally), and how to search for and uncomment lines in relevant&amp;nbsp;contexts.&lt;/p&gt;
&lt;p&gt;So, on with the show&amp;nbsp;…&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="stored-configurations"&gt;
&lt;h2&gt;Stored&amp;nbsp;Configurations&lt;/h2&gt;
&lt;p&gt;One major feature of pibootctl is that it&amp;#8217;s capable of backing up and restoring
boot configurations. It&amp;#8217;s relatively important to understand &lt;em&gt;how&lt;/em&gt; it does this
given that, as a relatively low level tool designed to mess with your boot
configuration, it&amp;#8217;s entirely possible to use it to &lt;em&gt;break&lt;/em&gt; your boot&amp;nbsp;configuration!&lt;/p&gt;
&lt;p&gt;The Pi&amp;#8217;s boot partition is a simple &lt;span class="caps"&gt;FAT&lt;/span&gt; partition at the start of the image,
and the boot configuration is (mostly) held in a series of straight-forward
text files. The great benefit of this is universal support: &lt;span class="caps"&gt;FAT&lt;/span&gt; is the
&amp;#8220;universal file-system&amp;#8221;; everything knows how to read and write it from Windows
to Mac to Linux, and even numerous embedded systems. Likewise, text files are
the &amp;#8220;universal format&amp;#8221;; every operating system has &lt;em&gt;some&lt;/em&gt; facility for editing
text files (which is the reason it was selected as the original format for the
throughly worthy &lt;a class="reference external" href="https://www.gutenberg.org/"&gt;Project Gutenberg&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Ultimately this means that, should the worst happen, and you screw up your boot
configuration, you can stick your &lt;span class="caps"&gt;SD&lt;/span&gt; card in another machine and edit it back
to a workable state. However, it might be easier if you know what a workable
state&amp;nbsp;is!&lt;/p&gt;
&lt;p&gt;pibootctl strives to be equally accessible in its backup strategy: boot
configurations are stored as &lt;span class="caps"&gt;ZIP&lt;/span&gt; archives of the original (mostly) text files,
on the &lt;span class="caps"&gt;FAT&lt;/span&gt; partition (in a sub-directory). &lt;span class="caps"&gt;ZIP&lt;/span&gt; is another pretty-much-universal
format (Windows, Mac &lt;span class="caps"&gt;OS&lt;/span&gt;, and most Linux distros have built-in support for
opening &lt;span class="caps"&gt;ZIP&lt;/span&gt; files), and by storing the backups on the &lt;span class="caps"&gt;FAT&lt;/span&gt; partition we ensure
that they remain accessible on all platforms&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;Assuming you&amp;#8217;ve &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/using-pibootctl.html"&gt;installed pibootctl on Ubuntu&lt;/a&gt; from the &lt;span class="caps"&gt;PPA&lt;/span&gt;. you can create a
backup of your current boot configuration using pibootctl like&amp;nbsp;so:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;save&lt;span class="w"&gt; &lt;/span&gt;original
&lt;/pre&gt;
&lt;p&gt;Why is sudo required? Simply because we&amp;#8217;re writing that backup to the boot
partition which only root has access to. After this you should be able to list
the available stored configurations like&amp;nbsp;so:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;┌──────────┬────────┬─────────────────────┐
│ Name     │ Active │ Timestamp           │
├──────────┼────────┼─────────────────────┤
│ original │ ✓      │ 2020-09-15 12:27:02 │
└──────────┴────────┴─────────────────────┘&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Note that sudo isn&amp;#8217;t required here as we&amp;#8217;re not writing anything to the boot
partition, just reading things. Also note that pibootctl has compared the
current boot configuration to the stored &amp;#8220;original&amp;#8221; configuration and noticed
they&amp;#8217;re exactly the same, hence the tick-mark in the &amp;#8220;Active&amp;#8221; column. Where
exactly is this&amp;nbsp;stored?&lt;/p&gt;
&lt;p&gt;In the pibootctl package in my &lt;span class="caps"&gt;PPA&lt;/span&gt;, pibootctl is configured to create a &amp;#8220;store&amp;#8221;
directory on the boot partition which will contain the stored&amp;nbsp;configurations:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/store&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;original.zip&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can install an appropriate archiving tool to take a look in this archive if
you want. Personally, I favour &amp;#8220;atool&amp;#8221; which provides generic commands for
working with a variety of archives (tar.gz, tar.xz, zip, etc.&amp;nbsp;etc.):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;atool&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;als&lt;span class="w"&gt; &lt;/span&gt;/boot/firmware/store/original.zip&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Archive:  /boot/firmware/store/nouboot.zip
pibootctl:0:e5bf92a57556aa24d4dd8c94ba63a755a21fdcc3

Do not edit the content of this archive; the line above is a hash of the
content which will not match after manual editing. Please use the pibootctl
tool to manipulate stored boot configurations
  Length      Date    Time    Name
---------  ---------- -----   ----
     1116  2020-09-15 12:27   config.txt
      327  2020-09-01 07:02   syscfg.txt
      200  2020-09-01 07:02   usercfg.txt
      127  2020-09-01 12:27   cmdline.txt
---------                     -------
     1770                     4 files&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Several points of interest&amp;nbsp;here:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;There&amp;#8217;s a strange &amp;#8220;pibootctl:0:xxxxxxx&amp;#8221; line at the top of the &lt;span class="caps"&gt;ZIP&lt;/span&gt; archive&amp;#8217;s
comment. This is a hash of the boot configuration in parsed order, and
pibootctl uses it to quickly determine which stored configurations match the
current boot&amp;nbsp;configuration.&lt;/li&gt;
&lt;li&gt;For this reason, there&amp;#8217;s a comment straight after the hash line warning users
not to modify the content of the archive, or the hash will no longer match.
At the moment, the tool doesn&amp;#8217;t use the hash to validate the archive contents
as I&amp;#8217;m on the fence as to whether permitting users to create their own
archives manually is a good or bad idea but for now the warning&amp;nbsp;stands.&lt;/li&gt;
&lt;li&gt;After this you can see the various files that make up the boot configuration:
config.txt, syscfg.txt, usercfg.txt, and cmdline.txt (which is referenced
from config.txt). Note that other files referenced by the boot configuration
like the kernel (vmlinuz) and the initrd (initrd.img) are &lt;em&gt;not&lt;/em&gt; included in
the archive. The assumption is that these things are under the control of the
package manager and shouldn&amp;#8217;t be restored with the boot configuration&amp;nbsp;itself.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="manipulation"&gt;
&lt;h2&gt;Manipulation&lt;/h2&gt;
&lt;p&gt;Now let&amp;#8217;s edit our boot configuration using pibootctl. We&amp;#8217;ll enable the camera
module and set &lt;span class="caps"&gt;GPU&lt;/span&gt; memory to&amp;nbsp;128Mb:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;camera.enabled&lt;span class="o"&gt;=&lt;/span&gt;on&lt;span class="w"&gt; &lt;/span&gt;gpu.mem&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;128&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Note once again we use &amp;#8220;sudo&amp;#8221;. Because we&amp;#8217;re changing files on the boot
partition we need root privileges. We can check the &amp;#8220;status&amp;#8221; output to see
these are definitely now&amp;nbsp;set:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;┌─────────────────────────┬──────────────────────┐
│ Name                    │ Value                │
├─────────────────────────┼──────────────────────┤
│ audio.enabled           │ on                   │
│ boot.devicetree.address │ 50331648 (0x3000000) │
│ boot.initramfs.filename │ ['initrd.img']       │
│ boot.kernel.64bit       │ on                   │
│ boot.kernel.cmdline     │ 'cmdline.txt'        │
│ boot.kernel.filename    │ 'vmlinuz'            │
│ camera.enabled          │ on                   │
│ gpu.mem                 │ 128 (Mb)             │
│ i2c.enabled             │ on                   │
│ serial.enabled          │ on                   │
│ spi.enabled             │ on                   │
│ video.framebuffer.max   │ 2                    │
│ video.overscan.enabled  │ off                  │
└─────────────────────────┴──────────────────────┘&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;In fact, the tool ensures that the written configuration matches the desired
configuration as part of its generation logic. Consider the following&amp;nbsp;configuration:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;config.txt&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="k"&gt;[pi4]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;max_framebuffers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;[all]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;5 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;cmdline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cmdline.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;7 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="na"&gt;include usercfg.txt&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;usercfg.txt&lt;/p&gt;
&lt;pre class="code ini literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;disable-bt&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this case &amp;#8220;usercfg.txt&amp;#8221;, a file that pibootctl is not permitted to edit,
disables the bluetooth module. What happens if we explicitly try to enable&amp;nbsp;it?&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--no-backup&lt;span class="w"&gt; &lt;/span&gt;bluetooth.enabled&lt;span class="o"&gt;=&lt;/span&gt;on&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Failed to set 3 setting(s)
Expected bluetooth.enabled to be True, but was False after being overridden
by usercfg.txt line 1
Expected serial.uart to be 1, but was 0 with no valid lines; this usually
means a setting like start_x or gpu_mem is in a file other than config.txt
Expected serial.enabled to be False, but was True with no valid lines; this
usually means a setting like start_x or gpu_mem is in a file other than
config.txt&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;pibootctl has noticed that it failed to set bluetooth.enabled, and warns the
user that the setting is &amp;#8220;overridden by usercfg.txt line 1&amp;#8221; (the other errors
come from the fact that disabling the bluetooth module implicitly enables the
serial port and places it on the &lt;span class="caps"&gt;PL011&lt;/span&gt; &lt;span class="caps"&gt;UART&lt;/span&gt;; these also don&amp;#8217;t match pibootctl&amp;#8217;s
expectations for the resulting configuration and so they get reported&amp;nbsp;too).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="restoration"&gt;
&lt;h2&gt;Restoration&lt;/h2&gt;
&lt;p&gt;How do we restore a saved configuration using pibootctl? The opposite of the
&amp;#8220;save&amp;#8221; command is&amp;nbsp;&amp;#8220;load&amp;#8221;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;nouboot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Backed up current configuration in backup-20200915-133659&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Once again, we need &amp;#8220;sudo&amp;#8221; as we&amp;#8217;re manipulating the content of the boot
partition. One additional thing to note above is that pibootctl automatically
saved a copy of the current boot configuration. It did this because, before
modifying the current configuration, it checked a copy of it existed in the
stored configurations. In this case no copy existed (since we changed it
above), so it automatically saved one for&amp;nbsp;us.&lt;/p&gt;
&lt;p&gt;You can suppress this behaviour with the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--no-backup&lt;/span&gt;&lt;/tt&gt; switch, or permanently
by modifying the tool&amp;#8217;s configuration in&amp;nbsp;&amp;#8220;/etc/pibootctl.conf&amp;#8221;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="don-t-panic"&gt;
&lt;h2&gt;Don&amp;#8217;t&amp;nbsp;Panic!&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s assume the worst has happened and you&amp;#8217;ve broken your boot configuration.
However, you&amp;#8217;ve got a stored configuration you know is good in one of these &lt;span class="caps"&gt;ZIP&lt;/span&gt;
archives. How do you restore it if you can no longer boot your Pi and access&amp;nbsp;pibootctl?&lt;/p&gt;
&lt;p&gt;Simply place the &lt;span class="caps"&gt;SD&lt;/span&gt; card in another machine, find the &lt;span class="caps"&gt;ZIP&lt;/span&gt; archive storing the
boot configuration you want (which will be under the &amp;#8220;store&amp;#8221; directory we
explored above), and extract it over the top of current boot configuration.
You&amp;#8217;ll almost certainly be prompted to replace the existing files (config.txt,
etc.) but that&amp;#8217;s fine: permit the replacement and you&amp;#8217;ve restored your boot
configuration! It really is that&amp;nbsp;simple.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="addendum-why-zips"&gt;
&lt;h2&gt;Addendum: Why&amp;nbsp;ZIPs?&lt;/h2&gt;
&lt;p&gt;I did debate just using sub-directories instead of &lt;span class="caps"&gt;ZIP&lt;/span&gt; archives and storing
configurations &amp;#8220;raw&amp;#8221; so users could simply copy them back without bothering
with unzip tools. This is certainly an attractive method,&amp;nbsp;but:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It makes it much easier for users to manipulate stored configurations in
place. As mentioned above, I&amp;#8217;m not (yet) convinced this is a good or bad
idea, so for now I&amp;#8217;ve opted for the method which makes it&amp;nbsp;harder.&lt;/li&gt;
&lt;li&gt;It makes it easy to store meta-data alongside the stored configuration (the
aforementioned hash). We &lt;em&gt;could&lt;/em&gt; do this in an auxilliary file in the stored
configuration but then we must be sure that could never exist in a legitimate
stored config. Or it could be in a file above the directory storing the
configuration, but then that has to be kept in sync with the stored
directories (which, as mentioned above, are easy to manipulate in their own&amp;nbsp;right).&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="next-time"&gt;
&lt;h2&gt;Next&amp;nbsp;Time&lt;/h2&gt;
&lt;p&gt;That&amp;#8217;s quite enough on this subject. Next time (hopefully less than months
away!) we&amp;#8217;ll take a look at the &amp;#8220;diff&amp;#8221; command, and how to integrate pibootctl
with more friendly tools on top of&amp;nbsp;it.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pibootctl"></category><category term="pi"></category></entry><entry><title>Using pibootctl</title><link href="https://waldorf.waveform.org.uk/2020/using-pibootctl.html" rel="alternate"></link><published>2020-05-20T00:00:00+01:00</published><updated>2020-10-04T00:58:22+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-05-20:/2020/using-pibootctl.html</id><summary type="html">&lt;p class="first last"&gt;The how of&amp;nbsp;pibootctl&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/introducing-pibootctl.html"&gt;previous post&lt;/a&gt;, we looked at &lt;em&gt;why&lt;/em&gt; I&amp;#8217;ve been working on pibootctl. But
if you just want to use it, look no further! (Or, do go and read the fine
&lt;a class="reference external" href="https://pibootctl.readthedocs.io/"&gt;manual&lt;/a&gt; if you&amp;nbsp;prefer).&lt;/p&gt;
&lt;p&gt;Installing it on your pi is pretty&amp;nbsp;simple:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;add-apt-repository&lt;span class="w"&gt; &lt;/span&gt;ppa:waveform/pibootctl&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pibootctl
&lt;/pre&gt;
&lt;p&gt;Side note: in future, this should be included by default on Ubuntu images, but
for now it lives in my &lt;span class="caps"&gt;PPA&lt;/span&gt;.&lt;/p&gt;
&lt;div class="section" id="help"&gt;
&lt;h2&gt;Help!&lt;/h2&gt;
&lt;p&gt;The utility includes several sub-commands, but the first you ought to learn (or
even guess) is&amp;nbsp;&amp;#8220;help&amp;#8221;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;usage: pibootctl [-h] [--version]
                 {help,?,status,dump,get,set,save,load,diff,show,cat,list,
                 ls,remove,rm,rename,mv} ...

pibootctl is a tool for querying and modifying the boot configuration of
the Raspberry Pi.

optional arguments:
  -h, --help            show this help message and exit
  --version             show program's version number and exit

commands:
  {help,?,status,dump,get,set,save,load,diff,show,cat,list,ls,remove,rm,
  rename,mv}
    help (?)            Displays help about the specified command or setting
    status (dump)       Output the current boot time configuration
    get                 Query the state of one or more boot settings
    set                 Change the state of one or more boot settings
    save                Store the current boot configuration for later use
    load                Replace the boot configuration with a saved one
    diff                Show the differences between boot configurations
    show (cat)          Show the specified stored configuration
    list (ls)           List the stored boot configurations
    remove (rm)         Remove a stored boot configuration
    rename (mv)         Rename a stored boot configuration&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can also ask this sub-command for help on any of the other sub-commands &amp;#8230;
including&amp;nbsp;itself:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;usage: pibootctl help [-h] [command-or-setting]

With no arguments, displays the list of pibootctl commands. If a command
name is given, displays the description and options for the named command.
If a setting name is given, displays the description and default value for
that setting.

positional arguments:
  command-or-setting  The name of the command or setting to output help for

optional arguments:
  -h, --help          show this help message and exit&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;If you fancy good old man-pages, there&amp;#8217;s those too. One for the tool itself,
and one for each sub-command. For&amp;nbsp;example:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;man&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;man&lt;span class="w"&gt; &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;These include some additional notes and examples which may be worth a look
(they&amp;#8217;re also included in the online &lt;a class="reference external" href="https://pibootctl.readthedocs.io/"&gt;manual&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="first-steps"&gt;
&lt;h2&gt;First&amp;nbsp;Steps&lt;/h2&gt;
&lt;p&gt;What about actual usage? The first thing you&amp;#8217;ll probably want to do is look at
the current state of your boot configuration. This is what Ubuntu 20.04&amp;#8217;s looks
like in pibootctl &amp;#8220;out of the&amp;nbsp;box&amp;#8221;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;┌─────────────────────────┬──────────────────────┐
│ Name                    │ Value                │
├─────────────────────────┼──────────────────────┤
│ audio.enabled           │ on                   │
│ boot.devicetree.address │ 50331648 (0x3000000) │
│ boot.kernel.64bit       │ on                   │
│ boot.kernel.cmdline     │ 'cmdline.txt'        │
│ boot.kernel.filename    │ 'uboot_rpi_3.bin'    │
│ i2c.enabled             │ on                   │
│ serial.enabled          │ on                   │
│ spi.enabled             │ on                   │
└─────────────────────────┴──────────────────────┘&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Several things to note&amp;nbsp;above:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Oooh,&amp;nbsp;pretty!&lt;/dt&gt;
&lt;dd&gt;The default output mode is designed to be human-readable and, if your
locale includes &amp;#8220;&lt;span class="caps"&gt;UTF&lt;/span&gt;-8&amp;#8221; then unicode line-drawing characters will be used
for&amp;nbsp;tables.&lt;/dd&gt;
&lt;dt&gt;You&amp;#8217;re using arm64 on a&amp;nbsp;Pi3?&lt;/dt&gt;
&lt;dd&gt;Right now, yes. It was the first card and Pi combination that came to&amp;nbsp;hand!&lt;/dd&gt;
&lt;dt&gt;There&amp;#8217;s not many settings&amp;nbsp;there?&lt;/dt&gt;
&lt;dd&gt;Yes, only modified settings are listed by default because otherwise there&amp;#8217;s
&amp;#8230; a lot (and most of them you probably don&amp;#8217;t care about). You can list
&lt;em&gt;all&lt;/em&gt; the settings with the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--all&lt;/span&gt;&lt;/tt&gt; option, but be warned there&amp;#8217;s more
than a page&amp;#8217;s&amp;nbsp;worth!&lt;/dd&gt;
&lt;dt&gt;I don&amp;#8217;t recognize those setting&amp;nbsp;names!&lt;/dt&gt;
&lt;dd&gt;Yes. This is probably going to be one of the more contentious decisions in
the design of pibootctl. I could&amp;#8217;ve stuck with the actual commands used in
the Pi&amp;#8217;s boot configuration, e.g. &amp;#8220;dtparam=spi=on&amp;#8221; or &amp;#8220;uart_enable=1&amp;#8221; or
&amp;#8220;disable_overscan=1&amp;#8221;, but I didn&amp;#8217;t. This probably needs justifying as it&amp;#8217;s
going to confuse anyone familiar with the existing settings (including&amp;nbsp;me!)&amp;#8230;&lt;/dd&gt;
&lt;/dl&gt;
&lt;div class="section" id="setting-names"&gt;
&lt;h3&gt;Setting&amp;nbsp;Names&lt;/h3&gt;
&lt;p&gt;The names for settings in the Pi&amp;#8217;s bootloader have grown more or less
organically over time and occasionally change as new interfaces emerge (e.g.
&amp;#8220;display_rotate&amp;#8221; being deprecated in favour of &amp;#8220;display_lcd_rotate&amp;#8221; and
&amp;#8220;display_hdmi_rotate&amp;#8221;). Moreover, the settings are for use in a fairly
low-level tool (the bootloader) so there&amp;#8217;s a general preference for defaults to
be &amp;#8220;0&amp;#8221;, hence the occasional setting with reverse setting logic&amp;nbsp;(&amp;#8220;disable_overscan=1&amp;#8221;).&lt;/p&gt;
&lt;p&gt;The lack of a common naming standard (while understandable) does make it tricky
to query groups of settings common to an area. For example, how would you query
all the video related settings (remembering that &amp;#8220;disable_overscan&amp;#8221; setting)?
However, if a naming scheme is imposed on the settings, it becomes&amp;nbsp;easy:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;ubuntu&amp;#64;pitest:~$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;--all&lt;span class="w"&gt; &lt;/span&gt;video.*&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;┌──────────────────────────────┬──────────┬────────────────────────────────┐
│ Name                         │ Modified │ Value                          │
├──────────────────────────────┼──────────┼────────────────────────────────┤
│ video.cec.enabled            │          │ on                             │
│ video.cec.init               │          │ on                             │
│ video.cec.name               │          │ 'Raspberry Pi'                 │
│ video.dispmanx.offline       │          │ off                            │
│ video.dpi.clock              │          │ off                            │
│ video.dpi.enabled            │          │ off                            │
│ video.dpi.format             │          │ 1 (9-bit RGB666; unsupported)  │
│ video.dpi.group              │          │ 0 (auto from EDID)             │
│ video.dpi.hsync.disabled     │          │ off                            │
│ video.dpi.hsync.phase        │          │ off                            │
│ video.dpi.hsync.polarity     │          │ off                            │
...&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The usual &amp;#8220;*&amp;#8221; and &amp;#8220;?&amp;#8221; &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Wildcard_character"&gt;wildcards&lt;/a&gt; can be used in the setting pattern. In the
above output it&amp;#8217;s worth noting that explanatory text is included for certain
setting values (e.g. &amp;#8220;auto from &lt;span class="caps"&gt;EDID&lt;/span&gt;&amp;#8221; for &amp;#8220;video.dpi.group&amp;#8221;). A more minimal
example is all the settings for the &lt;span class="caps"&gt;TV&lt;/span&gt;&amp;nbsp;output:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;ubuntu&amp;#64;pitest:~$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;--all&lt;span class="w"&gt; &lt;/span&gt;video.tv.*&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;┌─────────────────────┬──────────┬──────────┐
│ Name                │ Modified │ Value    │
├─────────────────────┼──────────┼──────────┤
│ video.tv.aspect     │          │ 1 (4:3)  │
│ video.tv.colorburst │          │ on       │
│ video.tv.enabled    │          │ on       │
│ video.tv.mode       │          │ 0 (NTSC) │
└─────────────────────┴──────────┴──────────┘&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This scheme also means that simple sorting by name groups related settings
together in the output. However, as mentioned above it is going to confuse
anyone who&amp;#8217;s familiar with the underlying setting names. To try and mitigate
this a bit, the help output for all settings includes the underlying command
name (or overlay and parameter name as appropriate), and the &amp;#8220;help&amp;#8221; command
itself will also accept such&amp;nbsp;names:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;disable_overscan&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;      Name: video.overscan.enabled
   Default: on
Command(s): disable_overscan

When enabled (the default), if a group 1 (CEA) HDMI mode is selected
&lt;/span&gt;&lt;span class="gp-VirtualEnv"&gt;(automatically or otherwise)&lt;/span&gt;&lt;span class="go"&gt;, the display output will include black borders
to align the edges of the output with a typical TV display.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;There are cases where an underlying command is represented (or affected by)
several settings (and vice versa). In this case, asking for help on such a
setting will tell you the names of all settings that can affect&amp;nbsp;it:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;start_file&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;start_file is affected by the following settings:

boot.debug.enabled
boot.firmware.filename
camera.enabled&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="scripting"&gt;
&lt;h2&gt;Scripting&lt;/h2&gt;
&lt;p&gt;We&amp;#8217;ve seen the pretty-printed output of multiple settings above. What about
retrieving an individual setting for scripting purposes? The &amp;#8220;get&amp;#8221; command is
used for this (there&amp;#8217;s very little point using this command outside a&amp;nbsp;script):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;serial.enabled&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;on&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;While this is simple enough, it&amp;#8217;s not very efficient when it comes to obtaining
the value of &lt;em&gt;lots&lt;/em&gt; of settings. To accommodate usage from scripts, the
majority of commands in pibootctl (including &amp;#8220;get&amp;#8221; and &amp;#8220;status&amp;#8221;) accept
the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--json&lt;/span&gt;&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--yaml&lt;/span&gt;&lt;/tt&gt;, or &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--shell&lt;/span&gt;&lt;/tt&gt; switches to produce output in &lt;span class="caps"&gt;JSON&lt;/span&gt;,
&lt;span class="caps"&gt;YAML&lt;/span&gt;, or a shell-compatible style&amp;nbsp;respectively:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;--json&lt;span class="w"&gt; &lt;/span&gt;--all&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;video.hdmi*&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;{&amp;quot;video.hdmi.edid.override&amp;quot;: false, &amp;quot;video.hdmi.safe&amp;quot;: false,
&amp;quot;video.hdmi.4kp60&amp;quot;: false, &amp;quot;video.hdmi.edid.ignore&amp;quot;: false,
&amp;quot;video.hdmi.edid.contenttype&amp;quot;: 0, &amp;quot;video.hdmi0.edid.filename&amp;quot;: &amp;quot;edid.dat&amp;quot;,
&amp;quot;video.hdmi.edid.parse&amp;quot;: true, &amp;quot;video.hdmi0.enabled&amp;quot;: null,
&amp;quot;video.hdmi.edid.3d&amp;quot;: false, &amp;quot;video.hdmi0.audio&amp;quot;: null,
&amp;quot;video.hdmi0.boost&amp;quot;: 5, &amp;quot;video.hdmi0.group&amp;quot;: 0, &amp;quot;video.hdmi0.timings&amp;quot;: [],
&amp;quot;video.hdmi.powersave&amp;quot;: false, &amp;quot;video.hdmi0.mode&amp;quot;: 0,
&amp;quot;video.hdmi0.mode.force&amp;quot;: false, &amp;quot;video.hdmi0.encoding&amp;quot;: 0,
&amp;quot;video.hdmi0.drive&amp;quot;: 0, &amp;quot;video.hdmi0.rotate&amp;quot;: 0, &amp;quot;video.hdmi0.flip&amp;quot;: 0,
&amp;quot;video.hdmi1.boost&amp;quot;: 5, &amp;quot;video.hdmi1.enabled&amp;quot;: null, &amp;quot;video.hdmi1.audio&amp;quot;:
null, &amp;quot;video.hdmi1.edid.filename&amp;quot;: &amp;quot;edid.dat&amp;quot;, &amp;quot;video.hdmi1.timings&amp;quot;: [],
&amp;quot;video.hdmi1.group&amp;quot;: 0, &amp;quot;video.hdmi1.mode&amp;quot;: 0, &amp;quot;video.hdmi1.mode.force&amp;quot;:
false, &amp;quot;video.hdmi1.encoding&amp;quot;: 0, &amp;quot;video.hdmi1.drive&amp;quot;: 0,
&amp;quot;video.hdmi1.rotate&amp;quot;: 0, &amp;quot;video.hdmi1.flip&amp;quot;: 0}
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;pibootctl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;--json&lt;span class="w"&gt; &lt;/span&gt;serial.enabled&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="writing"&gt;
&lt;h2&gt;Writing?&lt;/h2&gt;
&lt;p&gt;That&amp;#8217;s enough for this post; next time we&amp;#8217;ll have a look at how pibootctl
modifies the boot configuration, and the steps it takes to try and do so&amp;nbsp;safely!&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pibootctl"></category><category term="pi"></category></entry><entry><title>Introducing pibootctl</title><link href="https://waldorf.waveform.org.uk/2020/introducing-pibootctl.html" rel="alternate"></link><published>2020-04-27T00:00:00+01:00</published><updated>2020-10-04T00:58:22+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-04-27:/2020/introducing-pibootctl.html</id><summary type="html">&lt;p class="first last"&gt;The what and the why of&amp;nbsp;pibootctl&lt;/p&gt;
</summary><content type="html">&lt;p&gt;For the last few months I&amp;#8217;ve been working on a little tool called pibootctl.
The hope was to include that in the Focal release, but it couldn&amp;#8217;t quite make
the cut while so much work was going on with the boot sequence. Still, it&amp;#8217;s
available from a &lt;a class="reference external" href="https://launchpad.net/~waveform/+archive/ubuntu/pibootctl"&gt;&lt;span class="caps"&gt;PPA&lt;/span&gt;&lt;/a&gt; (&lt;tt class="docutils literal"&gt;ppa:waveform/pibootctl&lt;/tt&gt;) now for those that wish to
try it out, and &lt;a class="reference external" href="https://pibootctl.readthedocs.io/"&gt;the documentation&lt;/a&gt; is&amp;nbsp;online.&lt;/p&gt;
&lt;p&gt;This is the first in a short series of posts explaining some of the background
behind the tool, and what its future (hopefully!)&amp;nbsp;is.&lt;/p&gt;
&lt;div class="section" id="so-what-is-it"&gt;
&lt;h2&gt;So what is&amp;nbsp;it?&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s a command line tool for managing the Pi&amp;#8217;s boot&amp;nbsp;configuration.&lt;/p&gt;
&lt;div class="section" id="so-what-is-it-1"&gt;
&lt;h3&gt;So what is&amp;nbsp;it?&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s a utility for saving, restoring, and editing the various text files on the
boot&amp;nbsp;partition.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="so-what-is-it-2"&gt;
&lt;h3&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=TxWN8AhNER0"&gt;So what is&amp;nbsp;it?&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s a less friendly &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; … which doesn&amp;#8217;t do networking&amp;nbsp;either.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="only-joking"&gt;
&lt;h3&gt;Only&amp;nbsp;Joking!&lt;/h3&gt;
&lt;p&gt;Well, sort of. To be clear: it&amp;#8217;s &lt;em&gt;not&lt;/em&gt; intended to be a replacement for the
excellent &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; tool, however I &lt;em&gt;do&lt;/em&gt; intend it to form part of the
underlying kit for something vaguely&amp;nbsp;similar.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="why"&gt;
&lt;h2&gt;Why?!&lt;/h2&gt;
&lt;p&gt;Or more precisely &amp;#8220;why not just use or port &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt;?&amp;#8221;. I&amp;#8217;ve been asked
this pretty frequently so it&amp;#8217;s probably best to have somewhere I can point to
with all my arguments in a row, so here goes&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;Firstly, let&amp;#8217;s be clear: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-confgi&lt;/span&gt;&lt;/tt&gt; is great. It does exactly what it
says on the tin in a friendly, user-accessible way, and Ubuntu certainly needs
it, or something very much like it. So let&amp;#8217;s begin with why we can&amp;#8217;t &amp;#8220;just use&amp;nbsp;it&amp;#8221;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Ubuntu Server is pretty tightly wedded to &lt;a class="reference external" href="https://netplan.io/"&gt;netplan&lt;/a&gt; for its networking
configuration. Whether that&amp;#8217;s via the &lt;a class="reference external" href="https://github.com/CanonicalLtd/subiquity"&gt;subiquity&lt;/a&gt; installer on regular
server images, or via &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt; on our cloud and Pi&amp;nbsp;images.&lt;/li&gt;
&lt;li&gt;Raspbian is more traditional in its networking approach, and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt;
knows nothing about &lt;a class="reference external" href="https://netplan.io/"&gt;netplan&lt;/a&gt;. Now, that&amp;#8217;s not to say it won&amp;#8217;t work: it will
set up your &lt;span class="caps"&gt;WPA&lt;/span&gt; config, but it won&amp;#8217;t keep the netplan configuration in sync
so the next time you run &lt;tt class="docutils literal"&gt;netplan apply&lt;/tt&gt; it&amp;#8217;s overwritten. Not an ideal
user&amp;nbsp;experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Well, that doesn&amp;#8217;t sound so complex? We could patch &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; to affect
the netplan configuration instead couldn&amp;#8217;t&amp;nbsp;we?&lt;/p&gt;
&lt;p&gt;Yes we could, but now we&amp;#8217;re carrying a delta to the upstream &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt;
tool. Either someone needs to maintain that delta as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; evolves,
or we need to get that delta applied upstream. The former is undesirable, and
the latter is highly unlikely to happen (unless Raspbian wants to switch over
to using&amp;nbsp;netplan).&lt;/p&gt;
&lt;p&gt;Besides, it&amp;#8217;s not the only problem to&amp;nbsp;overcome:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Ubuntu&amp;#8217;s boot sequence on the Pi is in some ways more complex and in some
ways simpler than Raspbian&amp;#8217;s, but either way it&amp;#8217;s pretty &lt;em&gt;different&lt;/em&gt;. One
notable facet is that our &amp;#8220;config.txt&amp;#8221; file includes other files by&amp;nbsp;default.&lt;/li&gt;
&lt;li&gt;Unfortunately, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; doesn&amp;#8217;t handle includes in the boot
configuration, either for parsing or writing. In the face of includes, it
either doesn&amp;#8217;t know the state of existing settings (from includes), or will
fail to notice if its written settings are overridden in later&amp;nbsp;includes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Solving this means writing a fairly comprehensive parser for the &amp;#8220;config.txt&amp;#8221;
file (and its includes) and some logic to compare a &amp;#8220;desired&amp;#8221; configuration to
the &amp;#8220;effective&amp;#8221; configuration after writing changes to determine whether
settings have been overridden in later included&amp;nbsp;files.&lt;/p&gt;
&lt;p&gt;Funnily enough, that&amp;#8217;s exactly what pibootctl&amp;nbsp;does!&lt;/p&gt;
&lt;p&gt;Ah ha! Could pibootctl be used by &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; to fix these issues?
Potentially; I&amp;#8217;ve ensured there&amp;#8217;s absolutely nothing Ubuntu-specific in the
tool, and it&amp;#8217;s pure Python so there&amp;#8217;s nothing preventing it from running on
architectures we don&amp;#8217;t support like the Pi Zero, but we&amp;#8217;re getting ahead of&amp;nbsp;ourselves.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s finish off the issues with porting &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Setting up the boot options for desktop or &lt;span class="caps"&gt;CLI&lt;/span&gt;: we don&amp;#8217;t include a desktop on
our images, so that&amp;#8217;s out (or at the very least, becomes&amp;nbsp;conditional).&lt;/li&gt;
&lt;li&gt;Setting up auto-login and splash-screen. We don&amp;#8217;t have those either (on
classic, and there&amp;#8217;s … difficulties with adding a splash screen that are
sufficiently intricate I won&amp;#8217;t bore you with them&amp;nbsp;here).&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;VNC&lt;/span&gt;. Raspbian includes the extremely slick (but proprietary) Real-&lt;span class="caps"&gt;VNC&lt;/span&gt;, which
we don&amp;#8217;t have on Ubuntu. &lt;em&gt;Side note:&lt;/em&gt; personally, the one feature I&amp;#8217;d
&lt;em&gt;really&lt;/em&gt; like to see ported to one of the open source &lt;span class="caps"&gt;VNC&lt;/span&gt; implementations is
dispmanx capture so that things like the camera preview can pass over &lt;span class="caps"&gt;VNC&lt;/span&gt;, as
on Real-&lt;span class="caps"&gt;VNC&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Remote &lt;span class="caps"&gt;GPIO&lt;/span&gt;. Nope, we haven&amp;#8217;t got pigpio into the archive yet&amp;nbsp;either.&lt;/li&gt;
&lt;li&gt;Read-only &lt;span class="caps"&gt;FS&lt;/span&gt; via overlay. Again, there&amp;#8217;s … intricate issues&amp;nbsp;here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You get the picture. Even if we &lt;em&gt;did&lt;/em&gt; port &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt;, tweak it to use
&lt;a class="reference external" href="https://netplan.io/"&gt;netplan&lt;/a&gt;, and slot in &lt;a class="reference external" href="https://pibootctl.readthedocs.io/"&gt;pibootctl&lt;/a&gt; to handle the configuration re-writing,
we&amp;#8217;d still wind up with something inferior. Something the user would fire up
and immediately notice several options missing. Something that, even if we
included the &lt;span class="caps"&gt;VNC&lt;/span&gt; option, would work &lt;em&gt;quite&lt;/em&gt; as well as on&amp;nbsp;Raspbian.&lt;/p&gt;
&lt;p&gt;Not an ideal result, and we&amp;#8217;d &lt;em&gt;still&lt;/em&gt; be carrying a fat delta which would
require on-going maintenance every time upstream&amp;nbsp;changed.&lt;/p&gt;
&lt;div class="section" id="addendum"&gt;
&lt;h3&gt;Addendum&lt;/h3&gt;
&lt;p&gt;There&amp;#8217;s a flip-side to the above. While there&amp;#8217;s several things we&amp;#8217;d need to
drop from a ported &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt;, there&amp;#8217;s also plenty of stuff
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; doesn&amp;#8217;t do that we could trivially add given the capabilities
of the underlying tools on&amp;nbsp;Ubuntu:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;With netplan, we could have full Ethernet configuration options, IPv4 and
IPv6 settings, and&amp;nbsp;more.&lt;/li&gt;
&lt;li&gt;We tend to favour non-password &lt;span class="caps"&gt;SSH&lt;/span&gt; configurations; should we not add
facilities to import a user&amp;#8217;s &lt;span class="caps"&gt;SSH&lt;/span&gt;&amp;nbsp;keys?&lt;/li&gt;
&lt;li&gt;There&amp;#8217;s some facilities of pibootctl (which we&amp;#8217;ll be seeing in forthcoming
posts) that&amp;#8217;d be interesting to include&amp;nbsp;too!&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="so-how-then"&gt;
&lt;h2&gt;So … how&amp;nbsp;then?&lt;/h2&gt;
&lt;p&gt;That&amp;#8217;s enough argument about why porting &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; over to Ubuntu is not
as trivial as most think (or in some cases even desirable). But it&amp;#8217;s obvious to
everyone (even me!) that we do need &lt;em&gt;something&lt;/em&gt; user friendly for Pi
configuration in&amp;nbsp;Ubuntu.&lt;/p&gt;
&lt;p&gt;This is something I&amp;#8217;ll be working on this cycle, building on top of the tools
we&amp;#8217;ve got in Ubuntu. Which brings us (finally!) back to pibootctl. When I was
looking at the tools we&amp;#8217;ve got to play with for system configuration on Ubuntu,
which I could build upon for a raspi-config-esque tool, one major piece of the
jigsaw was missing: something to deal with&amp;nbsp;&amp;#8220;config.txt&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Not terribly surprising given it&amp;#8217;s entirely specific to the Pi but we did need
something to fill that gap. Initially, I&amp;#8217;d considered grabbing bits from
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; but when I checked it there were a couple of&amp;nbsp;problems:&lt;/p&gt;
&lt;p&gt;Firstly, it uses lua for re-writing the configuration. Nothing expressly wrong
with that, but lua isn&amp;#8217;t in &amp;#8220;main&amp;#8221; on Ubuntu and anything included by default
on our images (as such a raspi-config-esque tool eventually would) is meant to
be in &amp;#8220;main&amp;#8221; (not&amp;nbsp;&amp;#8220;universe&amp;#8221;).&lt;/p&gt;
&lt;p&gt;Moving stuff to main involves &lt;a class="reference external" href="https://wiki.ubuntu.com/MainInclusionProcess"&gt;a pretty thorough review&lt;/a&gt; process and wasn&amp;#8217;t
something I thought we could realistically do for a full-blown programming
language when it was only going to be used for a single thing. That already
necessitated re-writing a chunk of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;raspi-config&lt;/span&gt;&lt;/tt&gt; with something in &amp;#8220;main&amp;#8221;,
most likely&amp;nbsp;python.&lt;/p&gt;
&lt;p&gt;It can&amp;#8217;t handle configurations using includes, and for better or worse I&amp;#8217;d
already moved us over to a configuration that used them (in Eoan). Moving back
would be a nightmare of detection and manipulation logic in some package&amp;#8217;s
post-inst scripts (more complex than moving &lt;em&gt;to&lt;/em&gt; the split configuration in the
first place and I remember how nasty that was). Not to mention, I&amp;#8217;m still of
the opinion that the split configuration brings certain benefits (though that&amp;#8217;s
involved enough that it should probably be its own&amp;nbsp;post).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="not-now"&gt;
&lt;h2&gt;Not&amp;nbsp;now?&lt;/h2&gt;
&lt;p&gt;It feels like I&amp;#8217;ve been wittering on for way too long in this post, so I&amp;#8217;m
going to stop here, and leave the first play-through for next time! In the
meantime, anyone that wants to have a play, go have a look at the &lt;a class="reference external" href="https://launchpad.net/~waveform/+archive/ubuntu/pibootctl"&gt;&lt;span class="caps"&gt;PPA&lt;/span&gt;&lt;/a&gt; or
&lt;a class="reference external" href="https://pibootctl.readthedocs.io/"&gt;the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pibootctl"></category><category term="pi"></category><category term="raspi-config"></category></entry><entry><title>Ubuntu Desktops on the Pi</title><link href="https://waldorf.waveform.org.uk/2020/ubuntu-desktops-on-the-pi.html" rel="alternate"></link><published>2020-04-17T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-04-17:/2020/ubuntu-desktops-on-the-pi.html</id><summary type="html">&lt;p class="first last"&gt;A quick review of the Ubuntu desktop flavours on the Raspberry&amp;nbsp;Pi&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: A look at the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/6-months-with-the-pi-desktop.html"&gt;Hirsute desktop image&lt;/a&gt;, &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2022/making-jammy-less-dodgy.html"&gt;Jammy desktop image&lt;/a&gt;, and &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/desktop-on-a-stick.html"&gt;Making desktops with cloud-init&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the things I&amp;#8217;ve been looking at recently is the relative performance of
our desktop packages on the Pi (a lot of people seem to be trying to use the Pi
4 as a desktop work-horse - and it is &lt;em&gt;reasonably&lt;/em&gt; capable of this with the
right&amp;nbsp;selections).&lt;/p&gt;
&lt;p&gt;Anyway, here&amp;#8217;s some quick notes from my experiments. Firstly, the various
desktop packages and some comments (just in the order I tried&amp;nbsp;them):&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-desktop&lt;/span&gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;A bit sticky here and there (particularly during login and application
selection - basically anywhere Gnome tries to do any animation), but&amp;nbsp;tolerable.&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;xubuntu-desktop&lt;/span&gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;A bit slicker than the default on the Pi; could actually be a reasonable
choice for a default&amp;nbsp;desktop.&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lubuntu-desktop&lt;/span&gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;It&amp;#8217;s not pretty, but damn this flies! No real surprise there given it&amp;#8217;s
LXQt which is the basis of Raspbian too. With a bit of layout tweaking this
would definitely be my go to choice of desktop under Ubuntu on the&amp;nbsp;Pi.&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;kubuntu-desktop&lt;/span&gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Ouch, this is painful. Don&amp;#8217;t misunderstand me - I love Kubuntu generally
(I&amp;#8217;m one of those old stick-in-the-muds that bemoans all the options
that&amp;#8217;ve disappeared from Gnome over the years and loves that they&amp;#8217;re all
still there on &lt;span class="caps"&gt;KDE&lt;/span&gt;). However, it&amp;#8217;s undeniably heavier and on the Pi, even
on a 4Gb Pi 4, it&amp;#8217;s just&amp;nbsp;unusable.&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ubuntu-budgie-desktop&lt;/span&gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Oooh, pretty! Sadly, not that functional. The start menu is better than the
default but (shock! horror!) the terminal won&amp;#8217;t start, so that&amp;#8217;s this
experiment prematurely ended (yes, I could switch tty and fiddle around but
the Mac-like prettiness hasn&amp;#8217;t tempted me that much&amp;nbsp;:).&lt;/dd&gt;
&lt;/dl&gt;
&lt;div class="section" id="compatibility-notes"&gt;
&lt;h2&gt;Compatibility&amp;nbsp;notes&lt;/h2&gt;
&lt;p&gt;I was only testing the arm64 images; I&amp;#8217;d expect similar results under armhf
though. Several things are worth adding to improve compatibility or enable
hardware (these apply to pretty much all the desktops&amp;nbsp;above):&lt;/p&gt;
&lt;div class="section" id="graphics"&gt;
&lt;h3&gt;Graphics&lt;/h3&gt;
&lt;p&gt;Add &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dtoverlay=vc4-fkms-v3d&lt;/span&gt;&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;/boot/firmware/syscfg.txt&lt;/tt&gt; to enable the
&amp;#8220;fake&amp;#8221; &lt;span class="caps"&gt;KMS&lt;/span&gt; frame-buffer. Lots of &lt;span class="caps"&gt;GUI&lt;/span&gt; instability without this. For all those
that want this to be the default: so do I, but there&amp;#8217;s issues with doing this
that need solving first (breaks u-boot&amp;#8217;s video console, and breaks boot on the&amp;nbsp;3A+).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="audio"&gt;
&lt;h3&gt;Audio&lt;/h3&gt;
&lt;p&gt;From Focal (20.04) onwards, &lt;tt class="docutils literal"&gt;dtparam=audio=on&lt;/tt&gt; should be the default in the
boot configuration so audio should at least be present. However, if you&amp;#8217;re
installing a desktop environment you also want to tweak a pulse-audio parameter
to improve its scheduling and avoid choppy audio. Edit
&lt;tt class="docutils literal"&gt;/etc/pulse/default.pa&lt;/tt&gt; and find the&amp;nbsp;line:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
load-module module-udev-detect
&lt;/pre&gt;
&lt;p&gt;And append &lt;tt class="docutils literal"&gt;tsched=0&lt;/tt&gt; so that the line&amp;nbsp;reads:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
load-module module-udev-detect tsched=0
&lt;/pre&gt;
&lt;p&gt;(thanks to BranLoux896 on the &lt;a class="reference external" href="https://raspberrypi.org/forums/"&gt;Raspberry Pi forums&lt;/a&gt; for mentioning&amp;nbsp;this!)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="login-managers"&gt;
&lt;h3&gt;Login&amp;nbsp;managers&lt;/h3&gt;
&lt;p&gt;Most of the desktop packages provide a selection of display managers (gdm3,
lightdm, ssdm, etc.) on installation. I attempted most selections and found
that lightdm doesn&amp;#8217;t work for some reason I haven&amp;#8217;t tried to figure out yet,
but gdm3 and ssdm work&amp;nbsp;fine.&lt;/p&gt;
&lt;p&gt;However, one issue with gdm3: it always defaults to the &amp;#8220;ubuntu&amp;#8221; desktop even
if you&amp;#8217;ve installed, say, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lubuntu-desktop&lt;/span&gt;&lt;/tt&gt;. After selecting the user to
login as, click the gears at the bottom right and select the &lt;em&gt;actual&lt;/em&gt; desktop
you installed before completing the login&amp;nbsp;:)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="wireless-bluetooth"&gt;
&lt;h3&gt;Wireless &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt;&amp;nbsp;Bluetooth&lt;/h3&gt;
&lt;p&gt;Install the &amp;#8220;iw&amp;#8221; package (which&amp;#8217;ll pull in &amp;#8220;crda&amp;#8221;) and then run &lt;tt class="docutils literal"&gt;sudo iw reg
set &lt;span class="caps"&gt;GB&lt;/span&gt;&lt;/tt&gt; (if you&amp;#8217;re in the &lt;span class="caps"&gt;UK&lt;/span&gt; like me; substitute &lt;span class="caps"&gt;GB&lt;/span&gt; for your region code
otherwise) to set the WiFi region correctly (usually enables a few channels
that aren&amp;#8217;t enabled in the default &amp;#8220;world region&amp;#8221;). I&amp;#8217;ll endeavour to get this
in the image by default in Focal, but you&amp;#8217;ll still need to set the region code
for&amp;nbsp;now.&lt;/p&gt;
&lt;p&gt;Install the &amp;#8220;pi-bluetooth&amp;#8221; package to enable Bluetooth. This &amp;#8220;just works&amp;#8221; once
the package is&amp;nbsp;installed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="remote-desktops"&gt;
&lt;h3&gt;Remote&amp;nbsp;desktops&lt;/h3&gt;
&lt;p&gt;I ran a quick experiment in Terminal Services-style remote desktops with xrdp
under lubuntu-desktop (the only one I figured would be light enough to be
worthwhile for such endeavours). To try&amp;nbsp;this:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;install the &lt;tt class="docutils literal"&gt;xrdp&lt;/tt&gt; package&lt;/li&gt;
&lt;li&gt;add the &lt;tt class="docutils literal"&gt;xrdp&lt;/tt&gt; user to the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ssl-cert&lt;/span&gt;&lt;/tt&gt; group&lt;/li&gt;
&lt;li&gt;reboot&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then you should be able to connect remotely using an &lt;span class="caps"&gt;RDP&lt;/span&gt; client (like
&lt;tt class="docutils literal"&gt;remmina&lt;/tt&gt;) providing you specify the username and password in the connection&amp;nbsp;settings.&lt;/p&gt;
&lt;p&gt;Another quick experiment in screen-scraping style remote desktop was
semi-successful with the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;tigervnc-scraping-server&lt;/span&gt;&lt;/tt&gt; package but a further
experiment to do &lt;span class="caps"&gt;TS&lt;/span&gt;-style remote desktop with &lt;span class="caps"&gt;VNC&lt;/span&gt; wouldn&amp;#8217;t work (some issue
with &lt;span class="caps"&gt;GDM&lt;/span&gt; I didn&amp;#8217;t have time to dig&amp;nbsp;into).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="power-saving"&gt;
&lt;h3&gt;Power&amp;nbsp;saving&lt;/h3&gt;
&lt;p&gt;Power saving is &amp;#8230; rather pointless. I forget which desktop I saw this on, but
one of them decided to put the Pi to sleep on idle timeout and &amp;#8230; that just
effectively switched the Pi off! So, turn off all those suspend timers&amp;nbsp;:)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="lubuntu"></category><category term="xubuntu"></category><category term="kubuntu"></category><category term="budgie"></category><category term="hirsute"></category><category term="pi"></category><category term="desktop"></category><category term="bluetooth"></category><category term="wifi"></category><category term="vnc"></category><category term="rdp"></category></entry><entry><title>Package configuration</title><link href="https://waldorf.waveform.org.uk/2020/package-configuration.html" rel="alternate"></link><published>2020-04-16T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-04-16:/2020/package-configuration.html</id><summary type="html">&lt;p class="first last"&gt;Installing packages on first&amp;nbsp;boot&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/network-configuration.html"&gt;previous post&lt;/a&gt; we saw how to configure networking on first boot with
&lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt;. If your Pi has a reliable network connection during that first
boot (preferably Ethernet, given the current wifi issue described in &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/cloud-init/+bug/1870346"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;:
#1870346&lt;/a&gt;) you can also opt to have cloud-init install whatever packages
you&amp;#8217;re going to need (and configure them too!) so your system is completely
ready for you when you&amp;nbsp;login.&lt;/p&gt;
&lt;div class="section" id="updates"&gt;
&lt;h2&gt;Updates&lt;/h2&gt;
&lt;p&gt;First, it&amp;#8217;s useful to learn how to get the local package index up to date
(especially if you&amp;#8217;re dealing with an old image). This can be done by adding
the following line to the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; file (that one again!) on the boot&amp;nbsp;partition:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;If you also want to upgrade the system to the latest version of all installed
packages on the initial boot, you can&amp;nbsp;add:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="installation"&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;As you can tell, on Ubuntu these effectively run the &amp;#8220;apt update&amp;#8221; and &amp;#8220;apt
upgrade&amp;#8221; commands, but cloud-init also &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest/topics/availability.html"&gt;supports several other distros&lt;/a&gt;, hence
the rather generic naming of these options. How about installing extra
packages? This is also simple to&amp;nbsp;arrange:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="c1"&gt;# Install some things that Dave probably ought to consider including on&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;# the images by default...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;pi-bluetooth&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;iw&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can specify a particular version of a package by using a list, but this
generally isn&amp;#8217;t a good idea (there&amp;#8217;s no guarantee that version is going to be
available unless you&amp;#8217;re installing from, say, a repo you control like a &lt;span class="caps"&gt;PPA&lt;/span&gt;):&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;nginx-light&lt;/span&gt;&lt;span class="p-Indicator"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;1.17.9-0ubuntu3&lt;/span&gt;&lt;span class="p-Indicator"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Speaking of which: what if you want to include a &lt;span class="caps"&gt;PPA&lt;/span&gt; as a&amp;nbsp;source?&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;my-ppa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ppa:waveform/pibootctl&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# quote the source&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This may seem like an overly convoluted series of structures (&amp;#8220;source&amp;#8221; within a
label within &amp;#8220;sources&amp;#8221; within &amp;#8220;apt&amp;#8221;) but the reason for this is that there are
a great many &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest/topics/examples.html#additional-apt-configuration-and-repositories"&gt;options for apt&lt;/a&gt;, and a number of different means for specifying
third party repos (including importing of signing keys, and templates for
deriving new repos from existing&amp;nbsp;ones).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;p&gt;A couple more features are worth introducing in the context of installing
packages. Firstly, the ability to write arbitrary files. This is particularly
useful for providing configuration for packages you&amp;#8217;ve&amp;nbsp;installed:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;/etc/default/keyboard&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="no"&gt;# KEYBOARD CONFIGURATION FILE&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="no"&gt;XKBMODEL=&amp;quot;pc105&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="no"&gt;XKBLAYOUT=&amp;quot;gb&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="no"&gt;XKBVARIANT=&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="no"&gt;XKBOPTIONS=&amp;quot;ctrl: nocaps&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="no"&gt;BACKSPACE=&amp;quot;guess&amp;quot;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;And finally, the ultimate blunt tool of &amp;#8220;runcmd&amp;#8221; which effectively lets you run
arbitrary commands as root on the first boot; &lt;em&gt;use with caution&lt;/em&gt;! In this case
we can use it to set the wireless regulatory&amp;nbsp;domain:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;runcmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;iw reg set GB&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="putting-it-all-together"&gt;
&lt;h2&gt;Putting it all&amp;nbsp;together&lt;/h2&gt;
&lt;p&gt;Here&amp;#8217;s a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; file I use when testing Pi daily images. It sets up the
Pi so I can access it over &lt;span class="caps"&gt;SSH&lt;/span&gt;, importing my keys from GitHub, installs some
extra packages for WiFi and Bluetooth, configures the WiFi regulatory domain,
adds the &lt;span class="caps"&gt;PPA&lt;/span&gt; for &lt;a class="reference external" href="https://pibootctl.readthedocs.io/"&gt;pibootctl&lt;/a&gt; (a little project I&amp;#8217;ve been working on for boot
configuration&amp;nbsp;manipulation):&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;user-data&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="c1"&gt;#cloud-config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;chpasswd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ubuntu:ubuntu&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;pitest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;my-ppa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ppa:waveform/pibootctl&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# the quotes are mandatory&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;package_update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;package_upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;pi-bluetooth&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;iw&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;pibootctl&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;runcmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;iw reg set GB&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;ssh_pwauth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;gh:waveform80&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;29 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;write_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;31 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;/etc/default/keyboard&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;33 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;# KEYBOARD CONFIGURATION FILE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;XKBMODEL=&amp;quot;pc105&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;35 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;XKBLAYOUT=&amp;quot;gb&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;36 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;XKBVARIANT=&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;37 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;XKBOPTIONS=&amp;quot;ctrl: nocaps&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;38 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;BACKSPACE=&amp;quot;guess&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;39 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;40 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;runcmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;41 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;reboot&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;At the moment this file lives on my desktop and, after flashing a fresh image,
the first thing I do is copy that file onto the boot partition, replacing the
existing &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt;. Then I boot the Pi and leave it for a few minutes to
sort everything&amp;nbsp;out!&lt;/p&gt;
&lt;p&gt;Hopefully that gives you some ideas for playing with. And remember, you can
throw these same configurations at various cloud instances too (which can be
quite handy if you ever need to scale things up&amp;nbsp;temporarily!)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="coming-up"&gt;
&lt;h2&gt;Coming&amp;nbsp;Up&lt;/h2&gt;
&lt;p&gt;Next I&amp;#8217;m going to take a short break and see if I can set up some form of
comments here (probably via the lazy disqus route), and then I might delve into
a series on pibootctl or&amp;nbsp;something.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="cloud-init"></category></entry><entry><title>Network configuration</title><link href="https://waldorf.waveform.org.uk/2020/network-configuration.html" rel="alternate"></link><published>2020-04-10T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-04-10:/2020/network-configuration.html</id><summary type="html">&lt;p class="first last"&gt;How cloud-init talks to netplan to configure the&amp;nbsp;network&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/setting-up-users.html"&gt;previous post&lt;/a&gt; we saw how to configure multiple different users via
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt;, one of the &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt; configuration files. This time, we&amp;#8217;ll
turn our attention to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;network-config&lt;/span&gt;&lt;/tt&gt; which is probably the most important
configuration file aside from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; as it tells &lt;a class="reference external" href="https://netplan.io/"&gt;Netplan&lt;/a&gt; what to do
when setting up the&amp;nbsp;network.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;What&amp;#8217;s &lt;a class="reference external" href="https://netplan.io/"&gt;Netplan&lt;/a&gt;? It&amp;#8217;s a tool used on Ubuntu used to present a common
interface to whatever happens to manage the network. For instance, on
desktops this is typically Network Manager, while servers tend to use
systemd&amp;#8217;s networkd service. Netplan can &amp;#8220;render&amp;#8221; a configuration to work
with either of these&amp;nbsp;systems.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="network-config"&gt;
&lt;h2&gt;network-config&lt;/h2&gt;
&lt;p&gt;This file is basically just a typical &lt;a class="reference external" href="https://netplan.io/reference"&gt;Netplan configuration&lt;/a&gt; minus the root
&amp;#8220;network:&amp;#8221; value. By default it contains the following on the Pi&amp;nbsp;images:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title"&gt;network-config&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#wifis:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#  wlan0:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#    dhcp4: true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#    optional: true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#    access-points:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#      myhomewifi:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#        password: &amp;quot;S3kr1t&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#      myworkwifi:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#        password: &amp;quot;correct battery horse staple&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#      workssid:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#        auth:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#          key-management: eap&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#          method: peap&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#          identity: &amp;quot;me&amp;#64;example.com&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#          password: &amp;quot;passw0rd&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="c1"&gt;#          ca-certificate: /etc/my_ca.pem&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;On line 1, &amp;#8220;version:&amp;#8221; just declares the version of the Netplan schema that the
file conforms to (this should be&amp;nbsp;&amp;#8220;2&amp;#8221;).&lt;/p&gt;
&lt;p&gt;On line 2, the &amp;#8220;ethernets:&amp;#8221; value starts which sets up the &amp;#8220;eth0&amp;#8221; interface to
use &lt;span class="caps"&gt;DHCP&lt;/span&gt; for IPv4 (we haven&amp;#8217;t specified anything for IPv6) and marks the
interface optional (indicating we shouldn&amp;#8217;t wait for it to come up on boot,
just in case there&amp;#8217;s no cable plugged&amp;nbsp;in).&lt;/p&gt;
&lt;p&gt;On line 6 onwards is a big &amp;#8220;wifis:&amp;#8221; section which is commented out (the &amp;#8220;#&amp;#8221;
prefix), and which contains several examples of common WiFi configurations.
We&amp;#8217;ll explore this in the next&amp;nbsp;section.&lt;/p&gt;
&lt;p&gt;What should we do if we wanted to use a static &lt;span class="caps"&gt;IP&lt;/span&gt; configuration for eth0
instead of &lt;span class="caps"&gt;DHCP&lt;/span&gt;? The following is an example block which should be reasonably
obvious to anyone familiar with &lt;span class="caps"&gt;IP&lt;/span&gt;&amp;nbsp;networking.&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;192.168.0.20/24&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;gateway4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;192.168.0.1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;nameservers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;mydomain&lt;/span&gt;&lt;span class="p-Indicator"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;192.168.0.1&lt;/span&gt;&lt;span class="p-Indicator"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;8.8.8.8&lt;/span&gt;&lt;span class="p-Indicator"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;It is important to remember that &amp;#8220;addresses:&amp;#8221; takes a &lt;em&gt;list&lt;/em&gt; of addresses (as
an interface can have&amp;nbsp;multiple).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="wifi-configuration"&gt;
&lt;h2&gt;wifi&amp;nbsp;configuration&lt;/h2&gt;
&lt;p&gt;This is much the same as ethernet (as you can probably see from the commented
out example in the default file). The only major difference is the extra
&amp;#8220;access-points&amp;#8221; sub-value. Under this, you can list all the wifi access points
you regularly connect to, and the credentials required for&amp;nbsp;each.&lt;/p&gt;
&lt;p&gt;For example, let&amp;#8217;s assume your home router is called &amp;#8220;&lt;span class="caps"&gt;BT&lt;/span&gt;-1234&amp;#8221; with password &amp;#8220;a
very long password&amp;#8221;, but you also have an extender called &amp;#8220;wifi_ex&amp;#8221; which has
the (slightly less secure) password &amp;#8220;passw0rd&amp;#8221;. In this case you might use the
following&amp;nbsp;configuration:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;wifis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;wlan0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;access-points&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;BT-1234&amp;quot;&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;very&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;long&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;password&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;wifi_ex&amp;quot;&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;passw0rd&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;ethernets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;eth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;dhcp4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Note that it doesn&amp;#8217;t matter what order the top-level values appear in. I tend
to write &amp;#8220;version&amp;#8221; first, followed by &amp;#8220;ethernets&amp;#8221; but it really doesn&amp;#8217;t matter.
The same goes for sub-values of values; place them in whatever order you&amp;nbsp;want.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="later-changes"&gt;
&lt;h2&gt;Later&amp;nbsp;changes&lt;/h2&gt;
&lt;p&gt;What does cloud-init do with the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;network-config&lt;/span&gt;&lt;/tt&gt; file? Roughly speaking, it
copies it (with an additional root &amp;#8220;network:&amp;#8221; value) to
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/netplan/50-cloud-init.yaml&lt;/span&gt;&lt;/tt&gt; (the &lt;a class="reference external" href="https://netplan.io/reference"&gt;Netplan configuration&lt;/a&gt; consists of
all &lt;a class="reference external" href="https://yaml.org/"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&lt;/a&gt; files under &lt;tt class="docutils literal"&gt;/etc/netplan&lt;/tt&gt;) and then applies&amp;nbsp;it.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Unfortunately, there&amp;#8217;s currently a bug here. While cloud-init will happily
&lt;em&gt;copy&lt;/em&gt; your WiFi configuration to Netplan, it won&amp;#8217;t (currently) apply it
automatically. To do that, you need to reboot your Pi afterwards (or run
&lt;tt class="docutils literal"&gt;sudo netplan apply&lt;/tt&gt; from the command prompt, assuming you have a
keyboard&amp;nbsp;attached).&lt;/p&gt;
&lt;p class="last"&gt;The issue is being tracked under &lt;a class="reference external" href="https://bugs.launchpad.net/ubuntu/+source/cloud-init/+bug/1870346"&gt;&lt;span class="caps"&gt;LP&lt;/span&gt;: #1870346&lt;/a&gt; for those&amp;nbsp;interested.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For users of &lt;a class="reference external" href="https://www.raspberrypi.org/downloads/raspbian/"&gt;Raspbian&lt;/a&gt; this probably feels similar to its facility of
&lt;a class="reference external" href="https://www.raspberrypi.org/documentation/configuration/boot_folder.md"&gt;copying a file named wpa_supplicant.conf&lt;/a&gt; on the boot partition. The main
difference is that this deals with all interfaces rather than just&amp;nbsp;wifi.&lt;/p&gt;
&lt;p&gt;Remember that &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt; only deals with the very first boot. What happens
if you want to change your network configuration later? In this case simply
edit the file cloud-init created, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/netplan/50-cloud-init.yaml&lt;/span&gt;&lt;/tt&gt;, and run
&lt;tt class="docutils literal"&gt;sudo netplan apply&lt;/tt&gt; (or&amp;nbsp;reboot).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="back-to-user-data"&gt;
&lt;h2&gt;Back to&amp;nbsp;user-data&lt;/h2&gt;
&lt;p&gt;While we&amp;#8217;re looking at network related stuff, there&amp;#8217;s a couple of settings in
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; we should cover. First is &amp;#8220;hostname&amp;#8221; which can be used to &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest/topics/modules.html#set-hostname"&gt;set
the hostname&lt;/a&gt; of a freshly minted machine. It&amp;#8217;s as simple as specifying this
at the top&amp;nbsp;level:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;waldorf&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Whether this is honoured by the local &lt;span class="caps"&gt;DNS&lt;/span&gt; server will depend on its
configuration. If you use &lt;a class="reference external" href="http://www.thekelleys.org.uk/dnsmasq/doc.html"&gt;dnsmasq&lt;/a&gt; as your local &lt;span class="caps"&gt;DHCP&lt;/span&gt;/&lt;span class="caps"&gt;DNS&lt;/span&gt; server (as I do)
then it should work just&amp;nbsp;fine.&lt;/p&gt;
&lt;p&gt;System time can often be an issue on Pis, which lack a &lt;span class="caps"&gt;RTC&lt;/span&gt;. &lt;span class="caps"&gt;NTP&lt;/span&gt; is the usual
solution to this for any Pi connected to a network. Often, the &lt;span class="caps"&gt;DHCP&lt;/span&gt; server will
supply the address of an &lt;span class="caps"&gt;NTP&lt;/span&gt; server, or the &lt;span class="caps"&gt;NTP&lt;/span&gt;&amp;#8217;s default configuration will be
sufficient. However in some cases you may need to configure the &lt;span class="caps"&gt;NTP&lt;/span&gt; servers to
query manually. Again, you can use cloud-init to &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest/topics/modules.html#ntp"&gt;set &lt;span class="caps"&gt;NTP&lt;/span&gt; up&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;ntp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ntp.server.local&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ntp.ubuntu.com&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;192.168.23.2&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Cloud-init knows how to configure a reasonable variety of &lt;span class="caps"&gt;NTP&lt;/span&gt; clients and can
adapt the configuration above to their particular format. But how to get such a
client installed on the very first boot? That&amp;#8217;s a topic we&amp;#8217;ll leave for next&amp;nbsp;time&amp;#8230;&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="cloud-init"></category></entry><entry><title>Setting up users</title><link href="https://waldorf.waveform.org.uk/2020/setting-up-users.html" rel="alternate"></link><published>2020-04-08T00:00:00+01:00</published><updated>2022-09-06T13:27:19+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-04-08:/2020/setting-up-users.html</id><summary type="html">&lt;p class="first last"&gt;A deeper look at user creation in cloud-init&amp;#8217;s user-data&amp;nbsp;file&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2020/securing-your-ubuntu-pi.html"&gt;previous post&lt;/a&gt; we had a quick look at the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; configuration
file for &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt;. In this post, we&amp;#8217;ll have a more extensive look at some
of the other options for configuring users in this&amp;nbsp;file.&lt;/p&gt;
&lt;div class="section" id="different-passwords"&gt;
&lt;h2&gt;Different&amp;nbsp;Passwords&lt;/h2&gt;
&lt;p&gt;We&amp;#8217;ve seen how &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt; is told to set the initial user password, but
there&amp;#8217;s a few more possibilities there we haven&amp;#8217;t seen yet. Have a look at the
following&amp;nbsp;example:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="ln"&gt;1 &lt;/span&gt;&lt;span class="nt"&gt;chpasswd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;2 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;3 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;4 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ubuntu:ubuntu&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;5 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;root:$1$1dE3dcLz$8iWGkDxx9SCWdQfkPXbCE/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;6 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;pi:RANDOM&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Here, as last time, &amp;#8220;expire&amp;#8221; is false so you won&amp;#8217;t be prompted to change the
password on first login. We&amp;#8217;ve also specified a password for a pre-existing
user (root, not that this is necessarily encouraged on a system with &amp;#8220;sudo&amp;#8221;).
Finally, the special value &amp;#8220;&lt;span class="caps"&gt;RANDOM&lt;/span&gt;&amp;#8221; tells &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt; to generate a
completely random password for the &amp;#8220;pi&amp;#8221; user. This will be output on the
console, and in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/var/log/cloud-init-output.log&lt;/span&gt;&lt;/tt&gt; file.&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p&gt;You may want to erase (or at least restrict access to) the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cloud-init-output.log&lt;/span&gt;&lt;/tt&gt; file if you choose to use &lt;span class="caps"&gt;RANDOM&lt;/span&gt;&amp;nbsp;passwords.&lt;/p&gt;
&lt;p class="last"&gt;It is also worth noting (as the &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt; docs do) that providing
hashes of passwords is &lt;em&gt;not&lt;/em&gt; necessarily secure. You may want to try
running the hash above through something like &lt;a class="reference external" href="https://snapcraft.io/john-the-ripper"&gt;John the Ripper&lt;/a&gt; to
convince yourself of&amp;nbsp;this!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="different-users"&gt;
&lt;h2&gt;Different&amp;nbsp;Users&lt;/h2&gt;
&lt;p&gt;We&amp;#8217;ve already seen how &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt; creates a default &amp;#8220;ubuntu&amp;#8221; user, and for
many purposes that&amp;#8217;s enough. What would you do if you wanted to create other
users besides this, and can we import different sets of &lt;span class="caps"&gt;SSH&lt;/span&gt; keys to each user?
Here&amp;#8217;s another example &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="nt"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;robot&lt;/span&gt;&lt;span class="p-Indicator"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;robotics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;robot&lt;/span&gt;&lt;span class="p-Indicator"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;pi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;default&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;robot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;gecos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Mr. Robot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;primary_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;robot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="p-Indicator"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;gh:example&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;passwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;$5$hkui88$nvZgIle31cNpryjRfO9uArF7DYiBcWEnjqq7L1AQNN3&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The &amp;#8220;groups&amp;#8221; value lists the groups to create, and optionally the members they
should have. Here we set up three groups named &amp;#8220;robot&amp;#8221;, &amp;#8220;robotics&amp;#8221;, and &amp;#8220;pi&amp;#8221;.
The &amp;#8220;robot&amp;#8221; and &amp;#8220;robotics&amp;#8221; groups will each have a single member, the &amp;#8220;robot&amp;#8221;
user, while the &amp;#8220;pi&amp;#8221; group will start off with no&amp;nbsp;members.&lt;/p&gt;
&lt;p&gt;The &amp;#8220;users&amp;#8221; value lists all the users to create, along with their details. Here
we create two users: the default &amp;#8220;ubuntu&amp;#8221; user which is simply represented by
the &amp;#8220;default&amp;#8221; entry at the top. You can remove this if you don&amp;#8217;t want the
default&amp;nbsp;user.&lt;/p&gt;
&lt;p&gt;Then there&amp;#8217;s the aforementioned &amp;#8220;robot&amp;#8221; user. Various fields give this user an
initial password (in the form of a hash under &amp;#8220;passwd&amp;#8221;), tell the
&lt;a class="reference external" href="https://launchpad.net/ssh-import-id"&gt;ssh-import-id&lt;/a&gt; utility to import &lt;span class="caps"&gt;SSH&lt;/span&gt; keys from the &amp;#8220;example&amp;#8221; user on
&lt;a class="reference external" href="https://github.com/"&gt;GitHub&lt;/a&gt;, and additionally places the user in the &amp;#8220;users&amp;#8221;&amp;nbsp;group.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s an absolute pile of options that be specified for users; have a look at
the &lt;a class="reference external" href="https://cloudinit.readthedocs.io/en/latest/topics/modules.html#users-and-groups"&gt;Users and Groups&lt;/a&gt; section of the cloud-init docs for more&amp;nbsp;information.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="coming-up"&gt;
&lt;h2&gt;Coming&amp;nbsp;Up&lt;/h2&gt;
&lt;p&gt;Next, we&amp;#8217;ll take a break from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; to look at the other important
configuration file for &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt;: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;network-config&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="cloud-init"></category></entry><entry><title>Securing your Ubuntu Pi</title><link href="https://waldorf.waveform.org.uk/2020/securing-your-ubuntu-pi.html" rel="alternate"></link><published>2020-04-02T00:00:00+01:00</published><updated>2022-07-22T10:50:00+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2020-04-02:/2020/securing-your-ubuntu-pi.html</id><summary type="html">&lt;p class="first last"&gt;Using cloud-init configuration to secure your first&amp;nbsp;boot.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;One of the neat features of Raspbian is the ability to set up &lt;span class="caps"&gt;SSH&lt;/span&gt; out of the
box by placing an &lt;a class="reference external" href="https://www.raspberrypi.org/documentation/configuration/boot_folder.md"&gt;ssh file on the boot partition&lt;/a&gt;. This is a security feature
to ensure that your Pi isn&amp;#8217;t booted up with &lt;span class="caps"&gt;SSH&lt;/span&gt; for remote access enabled, plus
a widely known default username and password (pi / raspberry) which has full
&amp;#8220;sudo&amp;#8221; rights before you&amp;#8217;ve had a chance to &lt;a class="reference external" href="https://www.raspberrypi.org/documentation/configuration/security.md"&gt;change it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the surface, Ubuntu on the Pi has some similarities here, and some&amp;nbsp;differences:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;As with Raspbian, there&amp;#8217;s a default username and password (ubuntu /&amp;nbsp;ubuntu).&lt;/li&gt;
&lt;li&gt;As with Raspbian, the default user has full &amp;#8220;sudo&amp;#8221;&amp;nbsp;rights.&lt;/li&gt;
&lt;li&gt;Unlike Raspbian, &lt;span class="caps"&gt;SSH&lt;/span&gt; is enabled by default. This is an obvious security
problem given the above&amp;nbsp;points.&lt;/li&gt;
&lt;li&gt;To mitigate this, unlike Raspbian, Ubuntu forces you to change your password
on first&amp;nbsp;login.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Still, doesn&amp;#8217;t this leave a (hopefully brief) period of time when your Pi is
potentially network connected, with an active &lt;span class="caps"&gt;SSH&lt;/span&gt; daemon, and a widely known
default username and password combination with full root access? Yes &amp;#8230; it
does. Can we do better than this? Yes, with&amp;nbsp;&amp;#8230;&lt;/p&gt;
&lt;div class="section" id="cloud-init"&gt;
&lt;h2&gt;cloud-init&lt;/h2&gt;
&lt;p&gt;First, we&amp;#8217;ll see what I usually stick in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; configuration file
on a freshly flashed &lt;span class="caps"&gt;SD&lt;/span&gt; card, then we&amp;#8217;ll dissect it to see what&amp;#8217;s going on and
what else you can do with&amp;nbsp;it!&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;The file&amp;#8217;s name is literally &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; with no extension. It&amp;#8217;s
essentially a text file though, so feel free to open it in Notepad or any
other plain text&amp;nbsp;editor.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This file is part of the configuration for &lt;a class="reference external" href="https://cloudinit.readthedocs.io/"&gt;cloud-init&lt;/a&gt; and you&amp;#8217;ll find it on
the boot partition. This is the first partition on the card and, if you&amp;#8217;re
sticking the card in a Windows or Mac &lt;span class="caps"&gt;OS&lt;/span&gt; X machine, the only partition that&amp;#8217;ll
appear (because it&amp;#8217;s formatted &lt;span class="caps"&gt;FAT&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;Without further ado, here&amp;#8217;s the &lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;&amp;nbsp;version:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="nt"&gt;ssh_pwauth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;chpasswd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;ubuntu:ubuntu&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;What does all this &lt;a class="reference external" href="https://yaml.org/"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&lt;/a&gt;&amp;nbsp;mean?&lt;/p&gt;
&lt;p&gt;Line 1 configures &lt;span class="caps"&gt;SSH&lt;/span&gt; to disable password authentication, so that&amp;#8217;s our
security hole fixed (this happens before the default user is created). The
username and password may be well known but they&amp;#8217;re now useless for remote
access. Well that was easy, all done and &amp;#8230; oh, wait a minute. How should I
access the Pi remotely&amp;nbsp;now?&lt;/p&gt;
&lt;p&gt;Lines 3-4 take care of this. These tell cloud-init to use the incredibly useful
&lt;a class="reference external" href="https://launchpad.net/ssh-import-id"&gt;ssh-import-id&lt;/a&gt; utility to grab my public &lt;span class="caps"&gt;SSH&lt;/span&gt; keys from &lt;a class="reference external" href="https://launchpad.net/"&gt;Launchpad&lt;/a&gt; (that&amp;#8217;s
the &amp;#8220;lp:&amp;#8221; prefix, you can also use &amp;#8220;gh:&amp;#8221; for &lt;a class="reference external" href="https://github.com/"&gt;GitHub&lt;/a&gt;) and install them as
authorized keys for all users that cloud-init&amp;nbsp;creates.&lt;/p&gt;
&lt;p&gt;Lines 6-9 tell cloud-init that the &amp;#8220;ubuntu&amp;#8221; user (which it will create by
default) should have the password &amp;#8220;ubuntu&amp;#8221; and that I don&amp;#8217;t want it expired (so
it won&amp;#8217;t prompt me to change it on first login). Half the time I&amp;#8217;m dealing with
an ephemeral image I won&amp;#8217;t be keeping around anyway, so I don&amp;#8217;t much care about
changing the password, and on the occassions that I do want to keep the image,
I don&amp;#8217;t want to be bugged about the password on that first&amp;nbsp;login.&lt;/p&gt;
&lt;p&gt;And that&amp;#8217;s it! I boot my Pi for the first time (making sure it has an Ethernet
connection), wait a bit while cloud-init does its thing but, after about a
minute, my Pi has an &amp;#8220;ubuntu&amp;#8221; user I can access over &lt;span class="caps"&gt;SSH&lt;/span&gt; with public-key
authentication and no gaping security&amp;nbsp;holes.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;cloud-init performs &lt;em&gt;initial&lt;/em&gt; set up. That is to say, it only performs set
up on the very first boot. If you want to configure your Pi with cloud-init
you need to make changes to the configuration (detailed below) &lt;em&gt;before&lt;/em&gt;
booting your Pi for the first&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;If you wish to re-run cloud-init at a later time, you can do so with the
following command but be aware that some settings may not work as expected
(because users will already have been created, files already written, etc.&amp;nbsp;etc.):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cloud-init&lt;span class="w"&gt; &lt;/span&gt;clean&lt;span class="w"&gt; &lt;/span&gt;--logs&lt;span class="w"&gt; &lt;/span&gt;--reboot
&lt;/pre&gt;
&lt;p class="last"&gt;In general, consider this guide relevant for first boot situations &lt;em&gt;only&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="yaml-refresher"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&amp;nbsp;Refresher&lt;/h2&gt;
&lt;p&gt;In case you&amp;#8217;re wondering what the format of the file above is, it&amp;#8217;s &lt;a class="reference external" href="https://yaml.org/"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&lt;/a&gt;. It
may be useful to have a quick refresher course in &lt;a class="reference external" href="https://yaml.org/"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&lt;/a&gt; as that&amp;#8217;s what
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; (and all the other cloud-init configuration files) are written
in. Here&amp;#8217;s the file we saw above again, with some slight alterations for the
purposes of&amp;nbsp;demonstration:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="nt"&gt;ssh_pwauth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;ssh_import_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;lp:waveform&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;gh:foobar&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;chpasswd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;ubuntu&lt;/span&gt;&lt;span class="p-Indicator"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;pi&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;raspberry&lt;/span&gt;&lt;span class="p-Indicator"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;There are three top-level values: &amp;#8220;ssh_pwauth&amp;#8221;, &amp;#8220;ssh_import_id&amp;#8221;, and&amp;nbsp;&amp;#8220;chpasswd&amp;#8221;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;ssh_pwauth&amp;#8221; is a simple value set to &amp;#8220;false&amp;#8221;. In all cases, the name of a
value is written prior to a colon&amp;nbsp;(&amp;#8220;:&amp;#8221;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Next &amp;#8220;ssh_import_id&amp;#8221; is a &lt;em&gt;list&lt;/em&gt; value. There&amp;#8217;s two ways of writing lists in
&lt;a class="reference external" href="https://yaml.org/"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&lt;/a&gt; and this is the &amp;#8220;long-hand&amp;#8221; variant: &amp;#8220;-&amp;#8221; prefixed items, one per line
below the name of the&amp;nbsp;value:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;mylist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Item 1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Item 2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Item 3&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The other way of writing lists is the &amp;#8220;short-hand&amp;#8221; variant: comma-separated
values enclosed in square brackets. The list above could equally well be
written like&amp;nbsp;this:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;mylist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Item 1&lt;/span&gt;&lt;span class="p-Indicator"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Item 2&lt;/span&gt;&lt;span class="p-Indicator"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Item 3&lt;/span&gt;&lt;span class="p-Indicator"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Finally &amp;#8220;chpasswd&amp;#8221; is a compound value, containing two other values. This is
indicating by the indentation before the names of the two sub-values,
&amp;#8220;expire&amp;#8221; and&amp;nbsp;&amp;#8220;list&amp;#8221;&lt;/p&gt;
&lt;div class="admonition warning"&gt;
&lt;p class="first admonition-title"&gt;Warning&lt;/p&gt;
&lt;p class="last"&gt;You must &lt;em&gt;not&lt;/em&gt; mix indentation styles from one line to the next (as in
Python). When editing this file you are strongly recommended to stick to
spaces for indentation; do &lt;em&gt;not&lt;/em&gt; use the Tab key (unless you&amp;#8217;re certain
your editor is configured to expand tabs to&amp;nbsp;spaces)!&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;As with lists, there&amp;#8217;s a short-hand variant for compound values too:
command-separated elements enclosed in curly braces. The following two
declarations are equivalent in &lt;a class="reference external" href="https://yaml.org/"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;character&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p-Indicator"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Zaphod Beeblebrox&lt;/span&gt;&lt;span class="p-Indicator"&gt;,&lt;/span&gt;&lt;span class="nt"&gt; age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;42&lt;/span&gt;&lt;span class="p-Indicator"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nt"&gt;character&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Zaphod Beeblebrox&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;42&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;List items can be simple values, as we&amp;#8217;ve seen above or they can be compound
values themselves. For&amp;nbsp;example:&lt;/p&gt;
&lt;pre class="code yaml literal-block"&gt;
&lt;span class="nt"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Oberon&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;King of the fairies&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Titania&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Queen of the fairies&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Bottom&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;a weaver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;Robin Goodfellow&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l-Scalar-Plain"&gt;a mischievous sprite&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="coming-up"&gt;
&lt;h2&gt;Coming&amp;nbsp;Up&lt;/h2&gt;
&lt;p&gt;In the next post we&amp;#8217;ll take a look in more detail at the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;user-data&lt;/span&gt;&lt;/tt&gt; file and
see how to create multiple different users, assigning them different &lt;span class="caps"&gt;SSH&lt;/span&gt;&amp;nbsp;keys.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="ubuntu"></category><category term="pi"></category><category term="cloud-init"></category></entry></feed>