* Guix-μSD: setting up Guix on a μSD for the Librem 5
@ 2023-04-24 2:02 Zacchaeus Scheffer
0 siblings, 0 replies; only message in thread
From: Zacchaeus Scheffer @ 2023-04-24 2:02 UTC (permalink / raw)
To: help-guix
The following is my first draft of Guix-μSD, a literate program
detailing an optimal setup of guix home on PureOS for the Librem 5 (best
viewed in org-mode):
#! emacs --script
;; -*- org -*-
(find-file load-file-name) (org-mode) (org-babel-tangle) (kill-emacs)
Copyright (🄯) 2023 Zacchae.
Permission is granted to copy, distribute and/or modify these
documents under the terms of the GNU Free Documentation License,
Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts, no
Back-Cover Texts, and no History section.
Additionally, all code/functional structure contained in these
documents is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
* Introduction
This guide is a literate program to help you bootstrap a working
guix home install onto your Librem 5 running PureOS. Guix system
(Guix-SD) does not yet support the Librem 5, but you can still
install the guix package manager on the Librem 5. This process is
supposed to get you as close as possible to a Guix-SD system,
putting everything on a μSD card, thus I call this Guix-μSD. I
detail here everything I touched in PureOS, and leave the rest of
the guix hacking to you. The Librem 5 has only ~30GiB of internal
memory, so this process puts guix, as well as all of your personal
files, on a μSD, which I assume from here on that you have already
inserted. There is no garuntee that it will work, especially as
systems change and get updated, and you should understand/test each
line before running it. In theory, you should be able to open your
new Librem 5 and obtain this program by running (in ~):
#+begin_src shell :tangle no
sudo bash -c "apt update && apt upgrade -y && apt install git emacs -y"
git clone https://zacchae.us/zmacs
cd zmacs
./guid-usd.org
./guix-usd.bootstrap
reboot
#+end_src
And follow the prompts. However, https://zacchae.us is currently
down (check later for an updated version), and I have made some
changes to code here which haven't been tested, so doing so is
dangerous. After rebooting, you would finish setup with.
#+begin_src shell :tangle no
cd zmacs
./guix-usd.configure
# and optionally:
#./guix-usd.candy
#+end_src
Leaving you with a functional zmacs/home.scm which you can start
from. However, you may end up with a bricked device that must be
re-flashed, which is [[Re-flahsing from Guix][difficult to do from Guix]]. There will be no
warnings printed from the code, and I unashamedly pass "force" flags
to make dangerous operations go through. I do try to highlight
dangers in the text here, so it is "slightly" safer if you read
along and do things manually. Additionally, some operations are
performed (like appending to /etc/crypttab) that may break your
system if run twice without reverting files. If by some miracle,
you follow this guide and end up with a working device, you will
have a setup like the following:
- At boot, when you decrypt your Librem 5's eMMC memory (i.e. /), it
will attempt to decrypt your μSD using a key file located in /etc.
- If the μSD decrypts successfully, then it will mount btrfs
subvolumes from the decrypted μSD at /home, /gnu, and /var/guix.
- This means your home, the guix store, and current state of guix
are all stored on your μSD.
- If the μSD fails to decrypt, then:
- /gnu and /var/guix will fail to mount.
- guix will be unusable
- /home will be mounted from a file
** Final Warning
This guide has you modify the following files:
/etc/fstab
/etc/crypttab
Optionally modify the following:
/lib/systemd/system/guix-daemon.service
/etc/defaults/keyboard
/etc/passwd
Create the following files:
/opt/usd/*
/etc/luks-keys/*
/gnu
/var/guix
And completely destroy any data on your μSD.
This is your final warning to do a back up and pray.
* Initial setup
We will be setting up the entire μSD as a singular encrypted device
with a btrfs filesystem and subvolumes to hold guix and your files
#+begin_src shell :tangle guix-usd.bootstrap :shebang #!/bin/sh
sudo bash -c "apt update && apt upgrade -y && apt install btrfs-progs guix"
#+end_src
The μSD encryption keys will be in /etc/luks-keys, the special files
for mounting /home will be in /opt/usd, and we will need mount
points for guix folders /gnu and /var/guix
#+begin_src shell :tangle guix-usd.bootstrap
sudo mkdir /etc/luks-keys /opt/usd /gnu /var/guix
#+end_src
* Encrypting your μSD
To my knowledge, there is no way to prompt the user for a second
encryption passphrase for the μSD at boot, hence the only way to
decrypt the μSD at boot is to have a key file stored in your file
tree. This means that even if you remove your μSD, if you unlock
your phone, then the keys to your μSD could be extracted from your
phone. Additionally, the following will erase any data which was
previously on the μSD.
#+begin_src shell :tangle guix-usd.bootstrap
sudo dd if=/dev/urandom of=/etc/luks-keys/disk_secret_key bs=512 count=8
echo Encrypting device. Enter passphrase below (a few times).
echo This passphrase will be usable in addition to the key file at:
echo /etc/luks-key/disk_secret_key
sudo cryptsetup luksFormat /dev/sda --batch-mode
sudo cryptsetup luksAddKey /dev/sda /etc/luks-keys/disk_secret_key
#+end_src
* Setting up your btr file system
Open (map) the encrypted μSD, reformat the contents as btrfs (erases
all data), mount the mapped device, and create subvolumes for /home,
/gnu, and /var/guix. Additionally, copy over all files from your
home directory as a starting point for your new home directory.
#+begin_src shell :tangle guix-usd.bootstrap
sudo cryptsetup open /dev/sda crypt_sd --key-file=/etc/luks-keys/disk_secret_key
sudo mkfs.btrfs --force /dev/mapper/crypt_sd -L btros
sudo mount LABEL=btros /mnt
sudo btrfs subvolume create /mnt/home
sudo btrfs subvolume create /mnt/gnu
sudo btrfs subvolume create /mnt/varguix
sudo cp -a /home/purism /mnt/home/
sudo umount /mnt
#+end_src
* Setting up your backup /home
As mentioned previously, /home must be mounted. If the μSD does not
exist, then mount it from elsewhere. In this case, a file. I
allocate just 1GiB for the backup /home because I do not intend on
using it often, but need a useable space to recover from if
something (the μSD) fails. Copy the contents of the current /home
as well to be safe.
#+begin_src shell :tangle guix-usd.bootstrap
sudo dd if=/dev/zero of=/opt/usd/backup-home.btrfs bs=1MiB count=1024
sudo mkfs.btrfs /opt/usd/backup-home.btrfs
sudo mount /opt/usd/backup-home.btrfs /mnt
sudo btrfs subvolume create /mnt/home
cp -a /home/purism /mnt/home
#+end_src
* Decrypting/Mounting at boot
The following describes the steps to decrypt and mount your
now-set-up μSD at boot.
** Decrypting the μSD
To decrypt at boot, append the necessary entry to /etc/crypttab
#+begin_src shell :tangle guix-usd.bootstrap
echo crypt_sd UUID=$(sudo cryptsetup luksUUID /dev/sda) \
/etc/luks-keys/disk_secret_key nofail,luks \
| sudo tee -a /etc/crypttab
#+end_src
This will use the luks UUID to identify the μSD, and the 'nofail'
flag ensures the system will still boot if μSD is broken or
missing.
** Mounting /var/guix and /gnu
To mount your now-decrypted μSD, append the necessary lines to
/etc/fstab. /home will be treated in the [[Mounting /home][next section]]. /var/guix
and /gnu do not need to be accessable (guix will break) if the μSD
is not present, so use nofail, and wait no more than 20s for the
device to become available.
#+begin_src shell :tangle guix-usd.bootstrap
echo LABEL=btros /gnu btrfs \
nofail,x-systemd.device-timeout=15,subvol=gnu 0 2 \
| sudo tee -a /etc/fstab
echo LABEL=btros /var/guix btrfs \
nofail,x-systemd.device-timeout=15,subvol=varguix 0 2 \
| sudo tee -a /etc/fstab
#+end_src
** Mounting /home
This step was very tricky. I found a thread on the purism forum of
people trying to accomplish having a /home mounted from μSD, with a
fallback in case the μSD is broken or missing. That thread died
for a few years before I revived it with this hacky solution.
Basically, as far as I can tell, neither /etc/fstab or .mount
service units allow for any logic to determine if mount should
occur. If you specify a /home mount, and that mount fails, even if
nofail is specified, then the system will NOT let you try and
access files under /home. This means that even if you have a
perfectly fine home directory under the mount point, that you
cannot access those files. This means, among other things, that
Phosh will fail to start. However, though I couldn't figure out
how to change IF a mount point is used, I could change WHAT device
is used to mount. Specifically, .mount systemd units allow for
variable expansion, and variables can be read from a file.
Hence, my solution is to have a systemd service which is a
dependency of home.mount which generates an environment file which
is subsequently read by home.mount. The service checks if the μSD
was successfully decrypted, and if so, directs home.mount to mount
from the μSD. Otherwise, home is mounted from /opt/usd/home.btrfs.
Put the following in /etc/systemd/system/find-home.service
#+begin_src text :tangle /sudo::/etc/systemd/system/find-home.service
[Unit]
Description=Check if sd card was found. If so, use that.
[Service]
ExecStart=/opt/usd/find-home.sh
Type=oneshot
RemainAfterExit=yes
[Install]
RequiredBy=home.mount
#+end_src
Additionally, create the find-home.sh script which is to be run at
/opt/usd/find-home.sh
#+begin_src shell :shebang #!/bin/sh :tangle /sudo::/opt/usd/find-home.sh
echo HOME_DEVICE=$(if [ -e /dev/mapper/crypt_sd ];
then echo /dev/mapper/crypt_sd;
else echo /opt/usd/backup-home.btrfs;
fi) > /opt/usd/mount_env
#+end_src
Finally, create the home.mount which will actually mount your home
at /etc/systemd/system/home.mount
#+begin_src text :tangle /sudo::/etc/systemd/system/home.mount
[Unit]
Description=Mount Var Test
Requires=find-home.service
After=find-home.service
[Install]
RequiredBy=phosh.service
[Mount]
EnvironmentFile=/opt/usd/mount_env
What=${HOME_DEVICE}
Where=/home
Type=btrfs
Options=subvol=home
#+end_src
Finally, enable the home.mount service
#+begin_src shell :tangle guix-usd.bootstrap
systemctl enable home.mount
#+end_src
After doing the above, it +should+ might be safe to reboot
#+begin_src shell :tangle no
reboot
#+end_src
* Setting up guix home
This section assumes you have successfully done all the setps
described above and rebooted. First, you should do a guix pull as
your user, and additionally as root.
#+begin_src shell :tangle guix-usd.configure :shebang #!/bin/sh
guix pull
sudo -i ~/.config/guix/current/bin/guix pull
#+end_src
Now that root's guix has been setup, you should modify the systemd
unit file to use root's guix-daemon.
#+begin_src shell :tangle guix-usd.configure
sudo sed -i 's/\/usr\/bin\/guix-daemon/\/var\/guix\/profiles\/per-user\/root\/current-guix\/bin\/guix-daemon/' /lib/systemd/system/guix-daemon.service
systemctl daemon-reload
systemctl restart guix-daemon
#+end_src
This isn't strictly necessary, but I like that I can update
guix-daemon with sudo -i guix pull. This is also helpful because
the current (as of this writing) version of guix installed by apt
does not check bordeaux.guix.gnu.org for substitutes, something
extremely necessary for tiny arm devices that have little RAM. You
can, however, enable bordeaux.guix.gnu.org by adding
"--substitute-urls='https://bordeaux.guix.gnu.org
https://ci.guix.gnu.org'" to the "ExecStart" feild of
/lib/systemd/system/guix-daemon.service instead.
One other problem I came across is that guix home environment
variable initialization fails to properly deal with empty variables.
There are ways to fix this by modifying guix-home, but looking at
https://issues.guix.gnu.org/61982, it seems that they have decided
to make changes to how guix is installed on foreign distros. This
means even if you guix pull after this is patched, guix home will
probably not work properly until the next major release is adopted
in your apt repo.
Specifically, ~/.guix-home/setup-environment will assign to the
empty XDG_CONFIG_DIRS variable without adding the default "/etc/xdg"
value, so programs like phosh will not find /etc/xdg and fail to
start. This means if you naively install guix home on your new
Librem 5, you will get a black screen on boot...
To address this, I add the following line to my shell environment to
set XDG_CONFIG_DIRS to the default value in the case that it is
empty.
#+begin_src scheme :tangle no
("XDG_CONFIG_DIRS" . "$([ -z $XDG_CONFIG_DIRS ] 2> /dev/null && echo /etc/xdg || echo $XDG_CONFIG_DIRS)")
#+end_src
From here you should be able to use the following starter home.scm
at ./home.scm (not tested with bash).
#+begin_src scheme :tangle home.scm
(home-environment
(packages (map specification->package (list "emacs")))
(services
(list
(service
home-bash-service-type
(home-bash-configuration
(environment-variables
'(("XDG_CONFIG_DIRS" . "$([ -z $XDG_CONFIG_DIRS ] 2> /dev/null && echo /etc/xdg || echo $XDG_CONFIG_DIRS)")))))
(service
home-zsh-service-type
(home-zsh-configuration
(environment-variables
'(("XDG_CONFIG_DIRS" . "$([ -z $XDG_CONFIG_DIRS ] 2> /dev/null && echo /etc/xdg || echo $XDG_CONFIG_DIRS)"))))))))
#+end_src
And initialize your first home environment!
#+begin_src shell :tangle guix-usd.configure
guix home reconfigure home.scm
#+end_src
** Another note on substitutes
If trying to run guix home still results in many packages trying to
build, it could be that there was a recent push to the guix repo
which caused a lot of rebuilds that the build farm can't keep up
with. The only solution I know of is to manually check
https://ci.guix.gnu.org/jobset/master. Go there and find a commit
COMMIT right before a commit which resulted in a large number of
builds. Then run
#+begin_src shell :tangle no
guix pull --commit=COMMIT --allow-downgrades
#+end_src
This should revert your guix version to one with more substitutes
available.
* Finishing Touches
** Changing UI scaling
I do a lot of things at a terminal or emacs. I want my phone
screen to be like a real screen. For this, I change the UI scaling
of the builtin screen from 2 to 1 like so:
#+begin_src shell :tangle guix-usd.candy
echo '[output:DSI-1]
scale = 1' >> /usr/share/phosh/phoc.ini
#+end_src
** Changing console keyboard layout
I like to use the dvorak layout with caps-lock as another control
key. For the gui, you need to change your layout in settings,
download gnome-tweaks:
#+begin_src shell :tangle no
apt install gnome-tweaks
#+end_src
And enable "Caps Lock is also Ctrl" in gnome-tweaks>Keyboard &
Mouse>additional Layout Options>Caps Lock behavior. To make the
same layout available from a tty, I ran:
#+begin_src shell :tangle guix-usd.candy
sudo apt install console-data
echo '
XKBMODEL="pc105"
XKBLAYOUT="us"
XKBVARIANT="dvorak"
XKBOPTIONS="ctrl:nocaps"
BACKSPACE="guess"
' > /etc/default/keyboard
#+end_src
** Using zsh
I personally like zsh over bash for interactive prompts. To change
to zsh, run:
#+begin_src shell :tangle guix-usd.candy
sudo apt install zsh
echo Need user passwd to change shell to zsh
chsh -s /bin/zsh
#+end_src
* Re-flashing from Guix
If you messed something up, and know what you did, you may want to try fixing your problem using Jumpdrive instead:
https://github.com/dreemurrs-embedded/Jumpdrive
This will allow you to mount your powered off Librem like a flash drive and make changes to fix your device.
I don't have any automated solution for flashing a librem from guix, but I have figured out how to do it. To do so, I mostly followed the instructions at https://developer.puri.sm/Librem5/Development_Environment/Phone/Troubleshooting/Reflashing_the_Phone.html. First, I cloned the librem 5 flash image repository
#+begin_src shell :tangle no
git clone https://source.puri.sm/Librem5/librem5-flash-image
#+end_src
The main hiccup here is that the jenkins package required by the
flashing script has not been packaged for guix. To make it work, I
manually made changes to scripts/librem5-flash-image: comment lines
23-27, 461-484, and add `uboot_board = 'librem5'` to line 485 (line
numbers may have changed since).
Then, I manually tried to traverse https://arm01.puri.sm/ to find
some images, and downloaded the necessary images
#+begin_src shell :tangle no
mkdir imdir
wget https://arm01.puri.sm/job/Images/job/Image%20Build/14006/artifact/librem5r4.img.xz
xz -d librem5r4.img.xz
mv librem5r4.img imdir
wget https://arm01.puri.sm/job/u-boot_builds/job/uboot_librem5_build/lastSuccessfulBuild/artifact/output/uboot-librem5/u-boot-librem5.imx
mv u-boot-librem5.imx imdir/
#+end_src
Then, from a *ROOT SHELL*, I activated a guix shell, and downloaded
necessary packages from pip:
#+begin_src shell
cd librem5-flash-image
guix shel python gcc musl python-requests binutils usbutils uuu --pure
python3 -m venv venv --upgrade-deps --system-site-packages
source ./venv/bin/activate
pip3 install tqdm pyyaml
./scripts/librem5-flash-image --skip-download --dir ./imdir
#+end_src
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2023-04-24 5:39 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-24 2:02 Guix-μSD: setting up Guix on a μSD for the Librem 5 Zacchaeus Scheffer
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/guix.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.