Add config for Librem 5

This commit is contained in:
Alexander Bantyev 2023-02-14 02:23:27 +04:00 committed by Jörg Thalheim
parent 50dc4ef928
commit 9070d2340c
12 changed files with 424 additions and 0 deletions

View file

@ -232,6 +232,7 @@ See code for all available configurations.
| [PINE64 STAR64](pine64/star64/) | `<nixos-hardware/pine64/star64>` |
| [Purism Librem 13v3](purism/librem/13v3) | `<nixos-hardware/purism/librem/13v3>` |
| [Purism Librem 15v3](purism/librem/13v3) | `<nixos-hardware/purism/librem/15v3>` |
| [Purism Librem 5r4](purism/librem/5r4) | `<nixos-hardware/purism/librem/5r4>` |
| [Raspberry Pi 2](raspberry-pi/2) | `<nixos-hardware/raspberry-pi/2>` |
| [Raspberry Pi 4](raspberry-pi/4) | `<nixos-hardware/raspberry-pi/4>` |
| [Samsung Series 9 NP900X3C](samsung/np900x3c) | `<nixos-hardware/samsung/np900x3c>` |

View file

@ -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;

133
purism/librem/5r4/README.md Normal file
View file

@ -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 <path to librem 5's MMC>
```
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.<hostname>.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.

View file

@ -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 ];
};
}

View file

@ -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"
];
};
}

View file

@ -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 { })

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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.";
}];
}

View file

@ -0,0 +1 @@
with import <nixpkgs> { system = "aarch64-linux"; }; callPackage ./. { }

View file

@ -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

View file

@ -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
'';
}