From 9070d2340c16508e81e68e037a775618bb28b8d4 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Tue, 14 Feb 2023 02:23:27 +0400 Subject: [PATCH] Add config for Librem 5 --- README.md | 1 + flake.nix | 1 + purism/librem/5r4/README.md | 133 +++++++++++++++++++++ purism/librem/5r4/default.nix | 53 ++++++++ purism/librem/5r4/initrd.nix | 64 ++++++++++ purism/librem/5r4/kernel/6.1.10.nix | 14 +++ purism/librem/5r4/kernel/default.nix | 4 + purism/librem/5r4/librem5-base/default.nix | 43 +++++++ purism/librem/5r4/lockdown-fix.nix | 26 ++++ purism/librem/5r4/u-boot/build.nix | 1 + purism/librem/5r4/u-boot/default.nix | 70 +++++++++++ purism/librem/5r4/wifi.nix | 14 +++ 12 files changed, 424 insertions(+) create mode 100644 purism/librem/5r4/README.md create mode 100644 purism/librem/5r4/default.nix create mode 100644 purism/librem/5r4/initrd.nix create mode 100644 purism/librem/5r4/kernel/6.1.10.nix create mode 100644 purism/librem/5r4/kernel/default.nix create mode 100644 purism/librem/5r4/librem5-base/default.nix create mode 100644 purism/librem/5r4/lockdown-fix.nix create mode 100644 purism/librem/5r4/u-boot/build.nix create mode 100644 purism/librem/5r4/u-boot/default.nix create mode 100644 purism/librem/5r4/wifi.nix diff --git a/README.md b/README.md index 48018e5..b6a16dd 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,7 @@ See code for all available configurations. | [PINE64 STAR64](pine64/star64/) | `` | | [Purism Librem 13v3](purism/librem/13v3) | `` | | [Purism Librem 15v3](purism/librem/13v3) | `` | +| [Purism Librem 5r4](purism/librem/5r4) | `` | | [Raspberry Pi 2](raspberry-pi/2) | `` | | [Raspberry Pi 4](raspberry-pi/4) | `` | | [Samsung Series 9 NP900X3C](samsung/np900x3c) | `` | diff --git a/flake.nix b/flake.nix index 639bf03..cea6259 100644 --- a/flake.nix +++ b/flake.nix @@ -171,6 +171,7 @@ pine64-star64 = import ./pine64/star64; purism-librem-13v3 = import ./purism/librem/13v3; purism-librem-15v3 = import ./purism/librem/15v3; + purism-librem-5r4 = import ./purism/librem/5r4; raspberry-pi-2 = import ./raspberry-pi/2; raspberry-pi-4 = import ./raspberry-pi/4; kobol-helios4 = import ./kobol/helios4; diff --git a/purism/librem/5r4/README.md b/purism/librem/5r4/README.md new file mode 100644 index 0000000..f140ecb --- /dev/null +++ b/purism/librem/5r4/README.md @@ -0,0 +1,133 @@ +# Purism Librem 5 revision 4 + +Purism's [Librem 5] is a privacy-oriented Linux-friendly smartphone. + +[Librem 5]: https://puri.sm/products/librem-5/ + +## Installation procedure + +> *Note* +> +> TODO: build a uuu-compatible installer. + +Until there's a native installer, the easiest way to install NixOS on Librem 5 seems to be using [Jumpdrive]. + +[Jumpdrive]: https://github.com/dreemurrs-embedded/Jumpdrive + +### Jumpdrive + +Jumpdrive is a tiny Linux distribution which presents device's internal storage as USB mass storage when you connect it to a PC. +It also provides a shell session over telnet. + +Follow the instructions in the repo to boot into Jumpdrive. +Note that `uuu` is part of `nxpmicro-mfgtools` package in nixpkgs. + +Now, plug the device into your PC. A new block device representing Librem 5's internal MMC should appear in `/dev`. +Note down this device path. + +### U-Boot + +> **Note** +> +> While upstream u-boot does support Librem 5, it can only boot using `boot.scr`, for which NixOS has no native support. +> +> There's work on extlinux support in Librem 5's U-Boot here: https://source.puri.sm/a-wai/uboot-imx/-/tree/allow-compressed-kernel +> +> This U-Boot version is packaged in the [`u-boot`] directory. + + +[`u-boot`]: ./u-boot + +Provided you have a way to build Nix derivations for `aarch64-linux` (like a remote builder, [binfmt emulation], or you're building it on the phone itself), just run `nix-build u-boot/build.nix`. + +[binfmt emulation]: https://search.nixos.org/options?channel=22.11&show=boot.binfmt.emulatedSystems&from=0&size=50&sort=relevance&type=packages&query=binfmt + +> **Warning** +> +> Even though I've tested this myself, I can't guarantee that this will not render your device unbootable. +> Proceed with caution. +> +> If it does not work, your best bet is to follow the advice here, which will flash U-Boot build by upstream: https://forums.puri.sm/t/can-someone-with-serial-console-access-try-nixos-kernel-on-librem-5/19121/27 + +To flash the device, run + +```console +$ sudo u-boot-install-librem5 +``` + +At this point, if you have an OS installed on your Librem 5, it's best to reboot into it to check that the U-Boot was flashed correctly. +If that's the case, reboot back into Jumpdrive. + +### Partitioning + +Now, from your host system, partition the MMC. + +> **Warning** +> +> Doing this wipes all data off the phone + +I went with 1 bootable `ext2` partition for `/boot`, and one `ext4` partition for `/`. +It ended up looking like this (your device names will be different): + +```console +$ sudo fdisk -l /dev/mmcblk0 +Disk /dev/mmcblk0: 29.12 GiB, 31268536320 bytes, 61071360 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: 0xcec26c32 + +Device Boot Start End Sectors Size Id Type +/dev/mmcblk0p1 * 4096 499711 495616 242M 83 Linux +/dev/mmcblk0p2 499712 61071359 60571648 28.9G 83 Linux +``` + +Note 2MiB of free space before the first partition. +This is where U-Boot lives. + +Mount the partitions on your host system, e.g. to `/mnt` and `/mnt/boot`. +Remember that `/mnt` is the second partition, and `/mnt/boot` is the first. + +### Installation + +Now, write your NixOS config. +Use `/dev/mmcblk0p1` as `fileSystems."/boot"` and `/dev/mmcblk0p2` as `fileSystems."/"`. +Don't forget to import the [module from this directory](./default.nix). +If you plan to use the device as a smartphone, you have a choice of two "desktop" (?) environments packaged in nixpkgs: [phosh] and [Plasma Mobile]. + +[phosh]: https://search.nixos.org/options?channel=22.11&show=services.xserver.desktopManager.phosh.enable&from=0&size=50&sort=relevance&type=packages&query=phosh +[Plasma Mobile]: https://search.nixos.org/options?channel=22.11&show=services.xserver.desktopManager.plasma5.mobile.enable&from=0&size=50&sort=relevance&type=packages + +Build the configuration (`nix build .#nixosConfigurations..config.system.build.toplevel` if you're using flakes). + +Running `nixos-install --system ./result --root /mnt` will copy the system to the MMC. +Unless you're running on an aarch64 system, it will fail to activate or install the bootloader, however. +You must do this manually. +Get a shell on Jumpdrive, mount partitions there, and activate the system: + +```console +$ nc 172.16.42.1 23 +# mkdir /mnt +# mount /dev/mmcblk0p2 /mnt +# mkdir -p /mnt/boot +# mount /dev/mmcblk0p1 /mnt/boot +# chroot /mnt /nix/var/nix/profiles/system/activate +# chroot /mnt /nix/var/nix/profiles/system/bin/switch-to-configuration boot +``` + +Provided the last command succeeds, you now should have a bootable device. + +Unmount: + +```console +# sync +# umount /mnt/boot +# umount -l /mnt +# echo u > /proc/sysrq-trigger +# echo s > /proc/sysrq-trigger +``` + +And shut the phone down by holding the power key. + +Start it up and you should be booting straight into your NixOS installation. \ No newline at end of file diff --git a/purism/librem/5r4/default.nix b/purism/librem/5r4/default.nix new file mode 100644 index 0000000..f967439 --- /dev/null +++ b/purism/librem/5r4/default.nix @@ -0,0 +1,53 @@ +{ config, pkgs, lib, ... }: +let cfg = config.hardware.librem5; +in { + options = { + hardware.librem5 = { + wifiCard = lib.mkOption { + type = lib.types.enum [ "redpine" "sparklan" "none" ]; + description = lib.mdDoc '' + Which wi-fi card is installed in your phone. + + Phones shipped before Januarly 2023 have redpine, newer phones have sparklan. + ''; + default = "redpine"; + }; + customInitrdModules = lib.mkEnableOption (lib.mdDoc "use of custom kernel modules in the initrd."); + installUdevPackages = lib.mkEnableOption (lib.mdDoc "installation of udev packages from librem5-base."); + lockdownFix = lib.mkEnableOption (lib.mdDoc "fix for orientation and proximity sensors not working after lockdown."); + }; + }; + + imports = [ ./initrd.nix ./wifi.nix ./lockdown-fix.nix ]; + + config = { + hardware.librem5 = { + customInitrdModules = lib.mkDefault true; + installUdevPackages = lib.mkDefault true; + lockdownFix = lib.mkDefault true; + }; + + nixpkgs.overlays = [ + (import ./kernel) + (final: prev: { + ubootLibrem5 = final.callPackage ./u-boot { }; + + librem5-base = final.callPackage ./librem5-base { }; + }) + ]; + + boot = { + kernelParams = [ "rootwait" ]; + + loader = { + generic-extlinux-compatible.enable = lib.mkDefault true; + grub.enable = false; + }; + + kernelPackages = lib.mkDefault pkgs.linuxPackages_librem5; + }; + + services.udev.packages = lib.mkIf cfg.installUdevPackages [ pkgs.librem5-base ]; + + }; +} diff --git a/purism/librem/5r4/initrd.nix b/purism/librem/5r4/initrd.nix new file mode 100644 index 0000000..7ffe609 --- /dev/null +++ b/purism/librem/5r4/initrd.nix @@ -0,0 +1,64 @@ +{ config, pkgs, lib, ... }: +lib.mkIf config.hardware.librem5.customInitrdModules { + boot.initrd = { + kernelModules = [ + "bq25890_charger" + "dwc3" + "imx_dcss" + "imx_sdma" + "mtdblock" + "ofpart" + "phy_fsl_imx8mq_usb" + "snvs_pwrkey" + "spi_nor" + "tps6598x" + "xhci_hcd" + "usbcore" + "usb_storage" + "uas" + "xhci_plat_hcd" + ]; + # Not all default modules (e.g. SATA ones) are present in Librem 5 kernel fork + includeDefaultModules = false; + availableKernelModules = [ + "ahci" + + "sd_mod" + "sr_mod" + + "mmc_block" + + "uhci_hcd" + "ehci_hcd" + "ehci_pci" + "ohci_hcd" + "ohci_pci" + "xhci_pci" + "usbhid" + "hid_generic" + "hid_lenovo" + "hid_apple" + "hid_roccat" + "hid_logitech_hidpp" + "hid_logitech_dj" + "hid_microsoft" + "hid_cherry" + + "bq25890_charger" + "dwc3" + "imx_dcss" + "imx_sdma" + "mtdblock" + "ofpart" + "phy_fsl_imx8mq_usb" + "snvs_pwrkey" + "spi_nor" + "tps6598x" + "xhci_hcd" + "usbcore" + "usb_storage" + "uas" + "xhci_plat_hcd" + ]; + }; +} diff --git a/purism/librem/5r4/kernel/6.1.10.nix b/purism/librem/5r4/kernel/6.1.10.nix new file mode 100644 index 0000000..b68e0d0 --- /dev/null +++ b/purism/librem/5r4/kernel/6.1.10.nix @@ -0,0 +1,14 @@ +{ buildLinux, fetchFromGitLab, ... }@args: +buildLinux (args // rec { + defconfig = "librem5_defconfig"; + version = "6.1.10-librem5"; + modDirVersion = version; + src = fetchFromGitLab { + domain = "source.puri.sm"; + owner = "Librem5"; + repo = "linux"; + rev = "pureos/6.1.10pureos1"; + hash = "sha256-Cc16vMUcJ/a2k3zMynqZ99t1LyTSs7EXKdNGF6OTS1s="; + }; + kernelPatches = [ ]; +} // args.argsOverride or { }) diff --git a/purism/librem/5r4/kernel/default.nix b/purism/librem/5r4/kernel/default.nix new file mode 100644 index 0000000..be57b35 --- /dev/null +++ b/purism/librem/5r4/kernel/default.nix @@ -0,0 +1,4 @@ +final: prev: { + linuxPackages_librem5_6_1_10 = final.linuxPackagesFor (final.callPackage ./6.1.10.nix { }); + linuxPackages_librem5 = final.linuxPackages_librem5_6_1_10; +} diff --git a/purism/librem/5r4/librem5-base/default.nix b/purism/librem/5r4/librem5-base/default.nix new file mode 100644 index 0000000..a1724dc --- /dev/null +++ b/purism/librem/5r4/librem5-base/default.nix @@ -0,0 +1,43 @@ +{ stdenv, fetchFromGitLab, shellcheck, kmod, lib }: +stdenv.mkDerivation { + pname = "librem5-udev-rules"; + version = "unstable"; + src = fetchFromGitLab { + domain = "source.puri.sm"; + owner = "Librem5"; + repo = "librem5-base"; + rev = "f5b51beb144f76ef3bc483b74e19867bd6364d32"; + hash = "sha256-5k7e4o9ak0zik+XqRV6PPwkTDf3yH3NxtLkhTyCQj7U="; + }; + + buildPhase = ":"; + + checkInputs = [ shellcheck ]; + doCheck = true; + checkPhase = "make"; + + installPhase = '' + mkdir -p "$out/bin" "$out/lib/udev/rules.d" + cp -v default/lockdown-support/lockdown-support.sh "$out/bin" + chmod +x "$out/bin/lockdown-support.sh" + cp -v default/gpsd/99-gnss.rules "$out/lib/udev/rules.d" + + pushd debian + for rule in librem5-base-defaults.*.udev; do + cp -v "$rule" "$out/lib/udev/rules.d/''${rule#*.}.rules" + done + popd + ''; + + postFixup = '' + sed -i \ + -e "s@/usr/sbin/lockdown-support.sh@$out/bin/lockdown-support.sh@g" \ + -e "s@/usr/sbin/modprobe@${kmod}/bin/modprobe@g" \ + -e "s@/usr/sbin/rmmod@${kmod}/bin/rmmod@g" \ + "$out"/lib/udev/rules.d/*.udev.rules + ''; + + # https://source.puri.sm/Librem5/librem5-base/-/issues/68 + # President@Purism promised it's under a free license: https://matrix.to/#/%23community-librem-5%3Atalk.puri.sm/%24hNCtZr7Escmr56uz1eEiaHpakteEXig7b5G8t2W6tWs?via=balsoft.ru&via=matrix.org&via=shareknot.de&via=zorix.us + meta.license = lib.licenses.free; +} diff --git a/purism/librem/5r4/lockdown-fix.nix b/purism/librem/5r4/lockdown-fix.nix new file mode 100644 index 0000000..00e5041 --- /dev/null +++ b/purism/librem/5r4/lockdown-fix.nix @@ -0,0 +1,26 @@ +{ config, pkgs, lib, ... }: +lib.mkIf config.hardware.librem5.lockdownFix { + # We blacklist the drivers so they don't load during early boot when the sensors are disconnected, + boot.blacklistedKernelModules = [ + "st_lsm6dsx_spi" + "st_lsm6dsx_i2c" + "st_lsm6dsx" + ]; + + # and load them when the phone is fully booted; + systemd.services.librem5-lockdown-support = { + description = "Set up drivers for the orientation and proximity sensors on Librem 5"; + serviceConfig.Type = "oneshot"; + serviceConfig.ExecStart = "${pkgs.librem5-base}/bin/lockdown-support.sh"; + wantedBy = [ "default.target" ]; + path = [ pkgs.kmod ]; + }; + + # udev rules from librem5-base handle going into "lockdown mode" and back. + assertions = [{ + assertion = with config.hardware.librem5; + lockdownFix -> installUdevPackages; + message = + "'hardware.librem5.lockdownFix' requires 'hardware.librem5.installUdevPackages', but it is not enabled."; + }]; +} diff --git a/purism/librem/5r4/u-boot/build.nix b/purism/librem/5r4/u-boot/build.nix new file mode 100644 index 0000000..91b79d9 --- /dev/null +++ b/purism/librem/5r4/u-boot/build.nix @@ -0,0 +1 @@ +with import { system = "aarch64-linux"; }; callPackage ./. { } diff --git a/purism/librem/5r4/u-boot/default.nix b/purism/librem/5r4/u-boot/default.nix new file mode 100644 index 0000000..e8508ae --- /dev/null +++ b/purism/librem/5r4/u-boot/default.nix @@ -0,0 +1,70 @@ +{ stdenv, buildUBoot, fetchurl, fetchFromGitLab, lib, flex, bison }: +let + firmware-imx = stdenv.mkDerivation (fa: { + pname = "firmware-imx"; + version = "8.12"; + src = fetchurl { + url = "https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/${fa.pname}-${fa.version}.bin"; + sha256 = "1vr2wgjac718hp48arhdvxd7gib93zhdrbrla8w3xigc6szlfrvb"; + }; + unpackPhase = '' + cp $src firmware + chmod +x firmware + ./firmware --auto-accept + ''; + installPhase = '' + mkdir -p $out + cd ${fa.pname}-${fa.version}/firmware + cp ddr/synopsys/lpddr4_pmu_train_?d_?mem.bin hdmi/cadence/signed_*_imx8m.bin $out + ''; + meta.license = lib.licenses.unfree; + }); + + arm-trusted-firmware-imx8mq = stdenv.mkDerivation (fa: { + pname = "arm-trusted-firmware-bl31"; + version = "unstable-2020-07-08"; + src = fetchFromGitLab { + domain = "source.puri.sm"; + owner = "Librem5"; + repo = "arm-trusted-firmware"; + rev = "1fd3ff86cd4a05cd3e5637bf5a6902ac58fcafb9"; + hash = "sha256-fzpUxq+Hz7pijv5Mvzz+bUkaH79YSaugVUnViF7NB3A="; + }; + enableParallelBuilding = true; + hardeningDisable = [ "all" ]; + NIX_LDFLAGS = "--no-warn-rwx-segments"; + buildFlags = [ "PLAT=imx8mq" "bl31" ]; + installPhase = '' + mkdir -p $out + cp build/imx8mq/release/bl31.bin $out + ''; + dontStrip = true; + }); + + ubootLibrem5 = buildUBoot { + version = "2022.10-librem5.1"; + defconfig = "librem5_defconfig"; + src = fetchFromGitLab { + domain = "source.puri.sm"; + owner = "a-wai"; + repo = "uboot-imx"; + rev = "3a836701279ed1f51063dc5da6f59adc4809093e"; + hash = "sha256-69auZ8GzyhSBxzi4jc6IyyQ6JBrTYXaOk6dZ+joUgF4="; + }; + BL31 = "${arm-trusted-firmware-imx8mq}/bl31.bin"; + preConfigure = '' + cp $BL31 . + cp ${firmware-imx}/* . + ''; + preInstall = '' + cp flash.bin u-boot.imx + ''; + filesToInstall = [ "u-boot.imx" ]; + postInstall = '' + mkdir $out/bin + sed 's|TARGET="/usr/lib/u-boot/librem5.*"|TARGET="${placeholder "out"}"|' \ + $src/debian/bin/u-boot-install-librem5 > $out/bin/u-boot-install-librem5 + ''; + }; +in +ubootLibrem5 diff --git a/purism/librem/5r4/wifi.nix b/purism/librem/5r4/wifi.nix new file mode 100644 index 0000000..f469a4f --- /dev/null +++ b/purism/librem/5r4/wifi.nix @@ -0,0 +1,14 @@ +{ config, pkgs, lib, ... }: +lib.mkIf (config.hardware.librem5.wifiCard == "redpine") { + # Disable mainline rsi module + boot.blacklistedKernelModules = [ + "rsi_91x" + "rsi_sdio" + ]; + + # Load redpine in Wi-Fi station + BT dual mode + boot.extraModprobeConfig = '' + options redpine_91x dev_oper_mode=13 rsi_zone_enabled=1 antenna_diversity=1 + ''; + +}