Install GUIX on Macbook 12
Motivation
I have an old Intel macbook 12 of begin 2016, one of the so-called "Retina Macbooks". It is an ideal couch device but far too slow to run modern OS-X, provided you wanted to run that to begin with.
I wanted to revive this machine to avoid lugging the BIG laptop, especially for the friday SystemCrafters stream in my comfy chair in the living.
I saw some experience reports with Ubuntu, which worked, but then I never used it because Ubuntu is, well it is Ok, there is nothing wrong with it... Gnome works fine, KDE worked fine, it is just all different from my usual Arch/Guix machines for little added benefit.
Because I was curious about immutable distros and leveraging containers like toolbox/flatpak I installed Fedora Silverblue. Again it is fine, Gnome is really luxurious on this machine. It all works well, but again different. Distributing the software between immutable host (which you have to reboot to make packages appear after install), flatpaks and toolbox containers works fine again, but inevitably causes friction when crossing the domains, e.g. Emacs. Nothing insurmountable. Also I am not impresssed by toolbox, it is simple, but it does nothing that distrobox does not seem to do better, while being less tied to the OS. I am not a fan of the dev container workflow, and Fedora Silverblue didn't sell it to me. Again in large deployments the balance might be different.
AFAICT Guix offers (almost) the same immutability features and drawbacks but less pronounced. i.e. you do not have to reboot to make host software available in most cases. The diffferent profiles allow much more granular control with several half-way points to find the right balance of reproducability and creature comfort. Of course this is also means more opportunity to let it run out of control and create a confusing mess. In any case I can leverage the GUIX configuration of my servers and big laptop and ensure a consistent experience if I can get it on the lil' macbook and liberate it to fight another day.
Installation Process
Installing Ubuntu and Silverblue learned me that there is nothing specially difficult for the intel mac. It kinda just works with some small caveats.
A quick hardware check shows a BCM4350 wireless network adapter which is not on the open hardware list AFAIK, and since the laptop needs to do laptop duty and has only a single USB-C port WiFi is the only way to get something in and out of it. Intel graphics should not be a problem. An intel soundchip. Some Apple NVMe controller (foreshadowing)
So we probably need the nonguix version to support the firmware blobs.
Creating Installation Media
Check out on your local friendly Guix enabled device, a foreign
install is fine, checkout the SystemCrafters Guix Installer Repo. In
the root of the project is a script build.iso. Run it and have a
walk outside or something. It takes a while. If you have no Guix
enabled machine yet, you can download the last released version, which
is over a year old, so the first guix pull on the macbook will be
epic... .
You'll end up with a file guix-installer-202603162333.iso with of
course a probably newer timestamp is spacetime is behaving
consistently in your locati
This can be written to a thumb drive. Ensure you check with dmesg -w
which device the USB stick uses. On insertion you'll see
$ sudo dmesg -w
... lots of lines skipped
[308042.889695] usb 1-4.3.2: new high-speed USB device number 12 using xhci_hcd
[308043.000238] usb 1-4.3.2: New USB device found, idVendor=0781, idProduct=5572, bcdDevice= 1.00
[308043.000242] usb 1-4.3.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[308043.000244] usb 1-4.3.2: Product: Cruzer Switch
[308043.000246] usb 1-4.3.2: Manufacturer: SanDisk
[308043.000247] usb 1-4.3.2: SerialNumber: 4C530001061011107340
[308043.038906] usb-storage 1-4.3.2:1.0: USB Mass Storage device detected
[308043.039031] scsi host2: usb-storage 1-4.3.2:1.0
[308043.039112] usbcore: registered new interface driver usb-storage
[308043.042575] usbcore: registered new interface driver uas
[308044.049756] scsi 2:0:0:0: Direct-Access SanDisk Cruzer Switch 1.00 PQ: 0 ANSI: 6
[308044.055625] sd 2:0:0:0: [sda] 62521344 512-byte logical blocks: (32.0 GB/29.8 GiB)
[308044.056787] sd 2:0:0:0: [sda] Write Protect is off
[308044.056790] sd 2:0:0:0: [sda] Mode Sense: 43 00 00 00
[308044.057159] sd 2:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[308044.093399] sda: sda1 sda2
[308044.093545] sd 2:0:0:0: [sda] Attached SCSI removable disk
The '[sda]' on the last lines shows that the USB is now available as
/dev/sda. Be careful as using your current harddisk by accident will
cause an unscheduled backup/restore excercise. To be safe make sure
you use the data which appeared on screen when inserting the
drive. Pull it out and insert it again to be sure.
Also don't mount/open it if your smart desktop assistent comes asking helpfully. It only makes things slower and more complicated.
$ sudo dd if=guix-installer-202603162333.iso of=/dev/sda bs=1M
$ sync
The bs=1M is to speed things up otherwise it takes forever. Value is
not critical but this is easy to type and _reasonable_™.
This will probably take a refill coffee amount of time to complete. The `sync` should ensure the buffers are written before unplugging the device.
Installation Process
One of the biggest challenges to start the installation process is to build a Jenga tower of USB dongles to ensure you can power the macbook and insert the USB stick. This device only has a single USB-C port, it dislikes thunderbolt docks or even many USB-C docks. I used a Steamdeck dock. In practice I assume most of the cheap USB extender dongles will do it, however I had to experiment before it worked.
You can probably install the OS on whatever capacity is remaining after all this time, but I would not rely on it and I would not want to do the crypto setup of the disks with the battery draining like a James Bond Villain Death Trap.
I installed it a few times. First without disk encryption. Worked fine. You follow the prompts.
Then with disk encryption. Worked fine.
I choose separate volumes for root and home formatted in ext4. When encrypting the
volumes using LUKS we now get an interesting issue. During boot up we
are asked 4 times to unlock the drives. Also once we selected the
image to boot in Grub the kernel will ask to unlock the disks, but the
internal laptop keyboard does not work. This can be solved by adding a
USB keyboard to your Jenga Tower and unlock the drives. This get tired
really quickly. For installation it is not a blocker so we plod on.
Also at some point the graphical installer decide that black is the new orange and show nothing else than that. However the manual installer is well documented.
Also the installation process for GUIXSD is far less important than for other distros as you can completely control the OS using `guix system reconfigure`. So if you can boot in a terminal and get on your network you're golden.
Crypto Setup
I am going to forget the keyboard issue for now and deal with the passwords. Because if that is fixed the keyboard does not matter anymore as once the kernel is fully started it works again.
If you followed along the installer will already have created the partitioning and LUKS encryption of the volumes. We can use a keyfile to unlock the partitions but we have to make sure the keyfile is not exposed on an unencrypted volume. Some people suggest a keyfile on a USB stick but this is not very practical on this device (think Jenga tower).
There is no way to pass secrets from Grub to the booting
kernel. However we have access to files in the initial ramdisk. The
GUIX bootloader configuration has the extra-initrd option which was
originally added for this very purpose and adding the keyfile is
explained there.
So the plan is:
- add a key from a keyfile to a slot in the LUKS partitions
- wrap the keyfile in a cpio image to merge to the initial ramdisk
- configure the mapped devices to use this keyfile
add keyfile to initrd
I created a random file
$ dd if=/dev/urandom of=/keyfile.bin bs=1 count=512
$ chmod 0400 /keyfile.bin
$ echo /key-file.bin | cpio -oH newc >/key-file.cpio
$ chmod 0000 /keyfile.cpio
add the keyfile as a key to the LUKS partitions.
Update /etc/config.scm to add the extra-initrd option to make the file
accessible during early boot.
(bootloader (bootloader-configuration
(bootloader grub-efi-bootloader)
(targets '("/boot/efi"))
(keyboard-layout keyboard-layout)
(extra-initrd "/key-file.cpio")))
Tell kernel to use the keyfile
To configure the kernel to use the keyfile instead of asking for a
password, we have to add the #:key-file "/key-file.bin" to the luks mapping
(mapped-devices
(list (mapped-device
(source (uuid "47642581-ea45-4c53-84c8-d1baf81c70de"))
(target "my-root")
(type luks-device-mapping)
(arguments '(#:key-file "/key-file.bin")))
(mapped-device
(source (uuid "f2d3ee87-7cc7-4a95-8f4c-1f6c0fa750ee"))
(target "my-home")
(type luks-device-mapping)
(arguments '(#:key-file "/key-file.bin")))))
Apply it
run a guix system reconfigure and reboot and now you will only have to
enter 2x the password. A 50% improvement ( or 500%, 600% according to
some world leader).
Now why does grub insist in decrypting the /home partition?
A quick look in /boot/grub/grub.cfg shows:
➜ cat /boot/grub/grub.cfg | head
# This file was generated from your Guix configuration. Any changes
# will be lost upon reconfiguration.
insmod luks
insmod luks2
cryptomount -u 47642581ea454c5384c8d1baf81c70de
cryptomount -u f2d3ee877cc74a958f4c1f6c0fa750ee
# Set 'root' to the partition that contains /gnu/store.
Ok, that explains it. But why?
The manual does not give me any hints. Looking in the source code of the guix bootloader generator shows that it makes a list of all mapped devices which have a valid UUID and add it to grub. It throws a warning when a mapped device uses the regular linux devicename instead of the UUID as grub cannot find the devices with the linux name. This makes sense and we can abuse it to get rid of the 2nd password prompt:
(mapped-devices
(list (mapped-device
(source (uuid "47642581-ea45-4c53-84c8-d1baf81c70de"))
(target "my-root")
(type luks-device-mapping)
(arguments '(#:key-file "/key-file.bin")))
(mapped-device
;;(source (uuid "f2d3ee87-7cc7-4a95-8f4c-1f6c0fa750ee"))
(source "/dev/nvme0n1p3")
(target "my-home")
(type luks-device-mapping)
(arguments '(#:key-file "/key-file.bin")))))
If we replace the UUID with the regular device name, it will no longer
be exported to grub.cfg and the kernel kan still find the device for
mapping it.
A quick reconfigure and reboot later confirms this. I can now remove the keyboard from the Jenga tower, but that would be cheating.
Fixing the keyboard
Some searching in the Guix mailing list showed that "hid_apple" was removed from the standard initrd somewhere in '24 because of issues with RISCV.
That is where the initrd-modules option is for:
(operating-system
...
(initrd-modules (cons "hid_apple" %base-initrd-modules))
)
This fixes the issue. I forgot how I tested it, but it does.
Post Installation
System does not allow to login after sleep
Actually the problem is that the disk is still sleeping (remember the foreshadowing when seeing the Apple NVMe device). This is not GUIX specific as I already had to fix it in Ubuntu and Fedora. There you add
echo 0 >/sys/bus/pci/devices/0000\:01\:00.0/d3cold_allowed
in the crontab on the @reboot event.
This syntax is not supported by the herd scheduled jobs and I did not find an alternative and got no suggestions on the #guix IRC channel. So I decided to make a small shepherd service for this.
(define disk-sleep-shepherd-service
(shepherd-service
(provision '(disk-sleep))
(documentation "Disallow disk to go to d3cold state to prevent crash on wake up.")
(one-shot? #t)
(start #~(lambda _
(call-with-output-file "/sys/bus/pci/devices/0000:01:00.0/d3cold_allowed"
(lambda (port) (display "0" port)))
#t))))
The only part I struggled with is that the start field takes a
function with a variable number of arguments. The /var/log/messages
registered the error but was not very helpful. Herd did not show the
service at all until it was fixed. Comparing to other implementation
gave the needed clues.
To add this to the system:
(operating-system
...
(services
...
(simple-service 'disk-sleep-service shepherd-root-service-type
(list disk-sleep-shepherd-service))
...
))
Sound : no sound from speakers.
There is no sound coming from the speakers.
There is sound coming from the headphone jack. Good enough for use in the living.
I'll call this a feature, not a bug.
Things I would do differently
Basically use btrfs to have a single encrypted LUKS volume to deal with and split the volumes in btrfs. That would also give the other goodness of btrfs and should be well supported by grub.
But for now I'll enjoy my new couch laptop.