├── .gitignore ├── modules ├── util │ ├── generic-extlinux-compatible │ │ ├── extlinux-conf-builder.nix │ │ ├── default.nix │ │ └── extlinux-conf-builder.sh │ └── fdt-overlays.nix ├── lib │ └── default.nix └── default.nix ├── templates └── orangepi-5x │ ├── orangepi5.nix │ ├── orangepi5plus.nix │ ├── confguration.nix │ └── flake.nix ├── .github └── workflows │ ├── checks.yml │ └── images.yml ├── packages ├── linux │ └── xunlong.nix ├── default.nix ├── orangepi-firmware.nix └── libmali.nix ├── flake.lock ├── LICENSE ├── flake.nix └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | result -------------------------------------------------------------------------------- /modules/util/generic-extlinux-compatible/extlinux-conf-builder.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs }: 2 | 3 | pkgs.replaceVarsWith { 4 | src = ./extlinux-conf-builder.sh; 5 | isExecutable = true; 6 | replacements = { 7 | path = lib.makeBinPath [ 8 | pkgs.coreutils 9 | pkgs.gnused 10 | pkgs.gnugrep 11 | ]; 12 | inherit (pkgs) bash; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /templates/orangepi-5x/orangepi5.nix: -------------------------------------------------------------------------------- 1 | { ... }: { 2 | # Hardware can be turned on and off here 3 | board.hardware.enabled = { 4 | # If you are useing Orange Pi 5, uncomment the line below to turn the LED off 5 | # led = false; 6 | 7 | # Uncomment the line below to turn on the AP6275P Wi-Fi module 8 | # wifi-ap6275p = true; 9 | }; 10 | networking.hostName = "orangepi5"; 11 | } 12 | -------------------------------------------------------------------------------- /templates/orangepi-5x/orangepi5plus.nix: -------------------------------------------------------------------------------- 1 | { ... }: { 2 | # Hardware can be turned on and off here 3 | board.hardware.enabled = { 4 | # If you are using Orange Pi 5 Plus, uncomment the line below to turn the LEDs off 5 | # leds = false; 6 | 7 | # Uncomment the line below to turn on the AP6275P Wi-Fi module 8 | # wifi-ap6275p = true; 9 | }; 10 | networking.hostName = "orangepi5plus"; 11 | } 12 | -------------------------------------------------------------------------------- /modules/lib/default.nix: -------------------------------------------------------------------------------- 1 | { lib }: { 2 | mkOverlayOption = with lib; 3 | prefix: { 4 | overlay, 5 | inverse ? false, 6 | description ? mdDoc "Whether to enable ${overlay}" 7 | }: mkOption { 8 | inherit description; 9 | default = inverse; 10 | example = true; 11 | type = types.bool; 12 | apply = enabled: if enabled != inverse then [ "${prefix}/${overlay}.dtbo" ] else []; 13 | }; 14 | } -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | on: workflow_dispatch 2 | jobs: 3 | flake-check: 4 | name: "Flake checks" 5 | runs-on: ubuntu-24.04-arm 6 | steps: 7 | - name: "Checkout flake" 8 | uses: actions/checkout@v5 9 | - name: "Install nix" 10 | uses: cachix/install-nix-action@v31 11 | - name: "Use cachix" 12 | uses: cachix/cachix-action@v16 13 | with: 14 | name: socle 15 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 16 | - name: "Run checks" 17 | run: nix flake check 18 | -------------------------------------------------------------------------------- /packages/linux/xunlong.nix: -------------------------------------------------------------------------------- 1 | { linuxManualConfig, fetchFromGitHub, ubootTools, ... }: (linuxManualConfig rec { 2 | src = fetchFromGitHub { 3 | owner = "orangepi-xunlong"; 4 | repo = "linux-orangepi"; 5 | rev = "752c0d0a12fdce201da45852287b48382caa8c0f"; 6 | hash = "sha256-tVu/3SF/+s+Z6ytKvuY+ZwqsXUlm40yOZ/O5kfNfUYc="; 7 | }; 8 | version = "6.1.43-xunlong-rk35xx"; 9 | modDirVersion = "6.1.43"; 10 | extraMeta.branch = "6.1"; 11 | configfile = ./config/linux-${version}.config; 12 | allowImportFromDerivation = true; 13 | }).overrideAttrs (old: { 14 | nativeBuildInputs = old.nativeBuildInputs ++ [ ubootTools ]; 15 | }) 16 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1766147184, 6 | "narHash": "sha256-U0+Gm298qq8wLPAJA9ttoEqfuEkvVMkVP7DPXiOkU2s=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "fc1645d3d874df157636089d14364d0fc29dd5a4", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "repo": "nixpkgs", 15 | "type": "github" 16 | } 17 | }, 18 | "root": { 19 | "inputs": { 20 | "nixpkgs": "nixpkgs" 21 | } 22 | } 23 | }, 24 | "root": "root", 25 | "version": 7 26 | } 27 | -------------------------------------------------------------------------------- /packages/default.nix: -------------------------------------------------------------------------------- 1 | { self, pkgs, ... }: rec { 2 | linux-xunlong-rk35xx = linux-xunlong-rk35xx-6_1; 3 | linux-xunlong-rk35xx-6_1 = (pkgs.callPackage ./linux/xunlong.nix {}); 4 | 5 | orangepi-firmware = orangepi-firmware-2024_01_24; 6 | orangepi-firmware-2024_01_24 = (pkgs.callPackage ./orangepi-firmware.nix {}); 7 | 8 | mali-firmware-g610 = mali-firmware-g610-g21p0-01eac0; 9 | libmali-valhall-g610 = libmali-valhall-g610-g13p0; 10 | libmali-valhall-g610-g13p0 = libmali-valhall-g610-g13p0-x11-wayland-gbm; 11 | 12 | inherit (pkgs.callPackage ./libmali.nix {}) 13 | mali-firmware-g610-g21p0-01eac0 14 | libmali-valhall-g610-g13p0-x11-wayland-gbm; 15 | } 16 | -------------------------------------------------------------------------------- /packages/orangepi-firmware.nix: -------------------------------------------------------------------------------- 1 | { fetchFromGitHub, stdenvNoCC, ... }: stdenvNoCC.mkDerivation { 2 | pname = "orangepi-firmware"; 3 | version = "2024.01.24"; 4 | dontBuild = true; 5 | dontFixup = true; 6 | compressFirmware = false; 7 | 8 | src = fetchFromGitHub { 9 | owner = "orangepi-xunlong"; 10 | repo = "firmware"; 11 | rev = "76ead17a1770459560042a9a7c43fe615bbce5e7"; 12 | hash = "sha256-mltaup92LTGbuCXeGTMdoFloX3vZRbaUFVbh6lwveFs="; 13 | }; 14 | 15 | installPhase = '' 16 | runHook preInstall 17 | 18 | mkdir -p $out/lib/firmware 19 | cp -a * $out/lib/firmware/ 20 | 21 | runHook postInstall 22 | ''; 23 | } 24 | -------------------------------------------------------------------------------- /templates/orangepi-5x/confguration.nix: -------------------------------------------------------------------------------- 1 | { ... }: { 2 | # You can set your timezone here 3 | # time.timeZone = "Europe/Dublin"; 4 | 5 | nixpkgs = { 6 | # to build on aarch64 machine 7 | hostPlatform = { system = "aarch64-linux"; }; 8 | 9 | # to build on x86_64 machine 10 | # hostPlatform = { system = "aarch64-linux"; }; 11 | # buildPlatform = { system = "x86_64-linux"; }; 12 | }; 13 | 14 | nix.settings.experimental-features = [ "nix-command" "flakes" ]; 15 | services.openssh.enable = true; 16 | 17 | users.users.nixos = { 18 | isNormalUser = true; 19 | password = "nixos"; 20 | extraGroups = [ "wheel" ]; 21 | }; 22 | 23 | system.stateVersion = "24.11"; 24 | } 25 | -------------------------------------------------------------------------------- /templates/orangepi-5x/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; 4 | socle = { 5 | url = "github:dvdjv/socle"; 6 | inputs.nixpkgs.follows = "nixpkgs"; 7 | }; 8 | }; 9 | 10 | outputs = inputs @ {self, nixpkgs, socle, ...}: { 11 | nixosConfigurations = { 12 | orangepi5 = nixpkgs.lib.nixosSystem { 13 | specialArgs = { inherit inputs; }; 14 | modules = [ 15 | socle.nixosModules.orangepi-5 16 | ./confguration.nix 17 | ./orangepi5.nix 18 | ]; 19 | }; 20 | 21 | orangepi5plus = nixpkgs.lib.nixosSystem { 22 | specialArgs = { inherit inputs; }; 23 | modules = [ 24 | socle.nixosModules.orangepi-5-plus 25 | ./confguration.nix 26 | ./orangepi5plus.nix 27 | ]; 28 | }; 29 | }; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /modules/util/fdt-overlays.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: let 2 | dtCfg = config.hardware.deviceTree; 3 | in { 4 | disabledModules = [ "system/boot/loader/generic-extlinux-compatible" ]; 5 | imports = [ ./generic-extlinux-compatible ]; 6 | 7 | options = { 8 | hardware.deviceTree.enabledOverlays = with lib; mkOption { 9 | default = []; 10 | example = literalExpression '' 11 | [ 12 | "rockchip/overlay/rk3588-disable-led.dtbo" 13 | "rockchip/overlay/rk3588-wifi-ap6275p.dtbo" 14 | ] 15 | ''; 16 | type = types.listOf types.str; 17 | internal = true; 18 | description = mdDoc '' 19 | List of overlays to apply at runtime, relative to the dtb base. 20 | ''; 21 | }; 22 | }; 23 | 24 | config = lib.mkIf (dtCfg.enable) { 25 | system.extraSystemBuilderCmds = '' 26 | echo ${builtins.concatStringsSep " " dtCfg.enabledOverlays } > $out/devicetree-overlays 27 | ''; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 David Javakhishvili 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/images.yml: -------------------------------------------------------------------------------- 1 | on: workflow_dispatch 2 | jobs: 3 | build-images: 4 | name: "Build SD images" 5 | runs-on: ubuntu-24.04-arm 6 | steps: 7 | - name: "Checkout flake" 8 | uses: actions/checkout@v5 9 | - name: "Install nix" 10 | uses: cachix/install-nix-action@v31 11 | - name: "Use cachix" 12 | uses: cachix/cachix-action@v16 13 | with: 14 | name: socle 15 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 16 | - name: "Build Orange Pi 5 image" 17 | run: nix build ./templates/orangepi-5x#nixosConfigurations.orangepi5.config.system.build.sdImage 18 | - name: "Add Orange Pi 5 image artifact" 19 | uses: actions/upload-artifact@v4 20 | with: 21 | name: orangepi5-sd-image-aarch64-linux.img.zst 22 | path: result/sd-image/nixos-image-sd-card-*-aarch64-linux.img.zst 23 | - name: "Build Orange Pi 5 Plus image" 24 | run: nix build ./templates/orangepi-5x#nixosConfigurations.orangepi5plus.config.system.build.sdImage 25 | - name: "Add Orange Pi 5 Plus image artifact" 26 | uses: actions/upload-artifact@v4 27 | with: 28 | name: orangepi5-plus-sd-image-aarch64-linux.img.zst 29 | path: result/sd-image/nixos-image-sd-card-*-aarch64-linux.img.zst 30 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A collection of packages to support RK3588(S)-based SBCs"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs"; 6 | }; 7 | 8 | outputs = inputs @ { self, nixpkgs, ...}: let 9 | mkPkgs = system: ( 10 | import nixpkgs { 11 | localSystem = system; 12 | crossSystem = "aarch64-linux"; 13 | } 14 | ); 15 | 16 | forAllSystems = function: 17 | nixpkgs.lib.genAttrs [ 18 | "x86_64-linux" 19 | "aarch64-linux" 20 | ] (system: function (mkPkgs system)); 21 | in { 22 | packages = forAllSystems (pkgs: import ./packages { inherit self pkgs; } ); 23 | 24 | nixosModules = (import ./modules) self; 25 | 26 | checks = forAllSystems (pkgs: { 27 | orangepi-5 = (pkgs.nixos [ 28 | self.nixosModules.orangepi-5 29 | { system.stateVersion = "24.11"; } 30 | ]).config.system.build.sdImage; 31 | 32 | orangepi-5-plus = (pkgs.nixos [ 33 | self.nixosModules.orangepi-5-plus 34 | { system.stateVersion = "24.11"; } 35 | ]).config.system.build.sdImage; 36 | }); 37 | 38 | templates = rec { 39 | default = orangepi-5x; 40 | orangepi-5x = { 41 | path = ./templates/orangepi-5x; 42 | description = "A template for Orange Pi 5 family of SBCs"; 43 | }; 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /packages/libmali.nix: -------------------------------------------------------------------------------- 1 | { stdenv, stdenvNoCC, fetchFromGitHub, autoPatchelfHook, libdrm, wayland, libxcb, libX11, ... }: let 2 | src = fetchFromGitHub { 3 | owner = "JeffyCN"; 4 | repo = "mirrors"; 5 | rev = "ab3d91e3df2ef1c487c2d8f69daea1729668e428"; 6 | hash = "sha256-VBk1D41we3re9qcjDurtnFZIduARNdwd6RnDir7Xr3o="; 7 | }; 8 | in { 9 | mali-firmware-g610-g21p0-01eac0 = stdenvNoCC.mkDerivation { 10 | pname = "mali-firmware"; 11 | version = "g21p0-01eac0"; 12 | dontBuild = true; 13 | dontFixup = true; 14 | compressFirmware = false; 15 | 16 | inherit src; 17 | 18 | installPhase = '' 19 | runHook preInstall 20 | 21 | mkdir -p $out/lib/firmware 22 | install --mode=755 firmware/g610/mali_csffw.bin $out/lib/firmware/ 23 | 24 | runHook postInstall 25 | ''; 26 | }; 27 | 28 | libmali-valhall-g610-g13p0-x11-wayland-gbm = stdenv.mkDerivation rec { 29 | pname = "libmali-valhall-g610"; 30 | version = "g13p0"; 31 | variant = "x11-wayland-gbm"; 32 | dontConfigure = true; 33 | 34 | inherit src; 35 | 36 | nativeBuildInputs = [ autoPatchelfHook ]; 37 | buildInputs = [ stdenv.cc.cc.lib libdrm wayland libxcb libX11 ]; 38 | 39 | preBuild = '' 40 | addAutoPatchelfSearchPath ${stdenv.cc.cc.lib}/aarch64-unknown-linux-gnu/lib 41 | ''; 42 | 43 | installPhase = let 44 | libmaliFileName = "${pname}-${version}-${variant}.so"; 45 | in '' 46 | runHook preInstall 47 | 48 | mkdir -p $out/lib 49 | mkdir -p $out/etc/OpenCL/vendors 50 | mkdir -p $out/share/glvnd/egl_vendor.d 51 | 52 | install --mode=755 lib/aarch64-linux-gnu/${libmaliFileName} $out/lib 53 | echo $out/lib/${libmaliFileName} > $out/etc/OpenCL/vendors/mali.icd 54 | cat > $out/share/glvnd/egl_vendor.d/60_mali.json << EOF 55 | { 56 | "file_format_version" : "1.0.0", 57 | "ICD" : { 58 | "library_path" : "$out/lib/${libmaliFileName}" 59 | } 60 | } 61 | EOF 62 | 63 | runHook postInstall 64 | ''; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /modules/util/generic-extlinux-compatible/default.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | with lib; 4 | 5 | let 6 | blCfg = config.boot.loader; 7 | dtCfg = config.hardware.deviceTree; 8 | cfg = blCfg.generic-extlinux-compatible; 9 | 10 | timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout; 11 | 12 | # The builder used to write during system activation 13 | builder = import ./extlinux-conf-builder.nix { inherit lib pkgs; }; 14 | # The builder exposed in populateCmd, which runs on the build architecture 15 | populateBuilder = import ./extlinux-conf-builder.nix { 16 | inherit lib; 17 | pkgs = pkgs.buildPackages; 18 | }; 19 | in 20 | { 21 | options = { 22 | boot.loader.generic-extlinux-compatible = { 23 | enable = mkOption { 24 | default = false; 25 | type = types.bool; 26 | description = lib.mdDoc '' 27 | Whether to generate an extlinux-compatible configuration file 28 | under `/boot/extlinux.conf`. For instance, 29 | U-Boot's generic distro boot support uses this file format. 30 | 31 | See [U-boot's documentation](https://u-boot.readthedocs.io/en/latest/develop/distro.html) 32 | for more information. 33 | ''; 34 | }; 35 | 36 | useGenerationDeviceTree = mkOption { 37 | default = true; 38 | type = types.bool; 39 | description = lib.mdDoc '' 40 | Whether to generate Device Tree-related directives in the 41 | extlinux configuration. 42 | 43 | When enabled, the bootloader will attempt to load the device 44 | tree binaries from the generation's kernel. 45 | 46 | Note that this affects all generations, regardless of the 47 | setting value used in their configurations. 48 | ''; 49 | }; 50 | 51 | configurationLimit = mkOption { 52 | default = 20; 53 | example = 10; 54 | type = types.int; 55 | description = lib.mdDoc '' 56 | Maximum number of configurations in the boot menu. 57 | ''; 58 | }; 59 | 60 | populateCmd = mkOption { 61 | type = types.str; 62 | readOnly = true; 63 | description = lib.mdDoc '' 64 | Contains the builder command used to populate an image, 65 | honoring all options except the `-c ` 66 | argument. 67 | Useful to have for sdImage.populateRootCommands 68 | ''; 69 | }; 70 | 71 | }; 72 | }; 73 | 74 | config = let 75 | builderArgs = "-g ${toString cfg.configurationLimit} -t ${timeoutStr}" 76 | + lib.optionalString (dtCfg.name != null) " -n ${dtCfg.name}" 77 | + lib.optionalString (!cfg.useGenerationDeviceTree) " -r"; 78 | in 79 | mkIf cfg.enable { 80 | system.build.installBootLoader = "${builder} ${builderArgs} -c"; 81 | system.boot.loader.id = "generic-extlinux-compatible"; 82 | 83 | boot.loader.generic-extlinux-compatible.populateCmd = "${populateBuilder} ${builderArgs}"; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # socle 2 | [Wikipedia](https://en.wikipedia.org/wiki/Socle_(architecture)): In architecture, a socle is a short plinth used to support a pedestal, sculpture, or column. 3 | 4 | A collection of packages and modules to support NixOS on single-board computers based on the RK3588(S) SoC. Currently, the following boards are supported: 5 | * Orange Pi 5 6 | * Orange Pi 5 Plus 7 | 8 | ## Features 9 | * Linux Kernel 6.1 10 | * OpenCL 3.0 with libmali 11 | * Options to enable device tree overlays 12 | 13 | ## Usage 14 | Socle provides a template you can use as a base for your new NixOS installation. 15 | 16 | ### Building an SD Image 17 | The easiest way to build an image is to use the provided flake template. The template contains NixOS configurations for Orange Pi 5 and Orange Pi 5 Plus. 18 | 19 | 1. Clone the template: 20 | ``` 21 | nix flake new --template github:dvdjv/socle nixos 22 | ``` 23 | This will create a directory named `nixos` with the system configurations in it. 24 | 2. (Optional) Edit the configuration: 25 | 26 | Open the file `nixos/configuration.nix` in a text editor. To change the default user credentials, locate the following lines: 27 | ``` 28 | users.users.nixos = { 29 | isNormalUser = true; 30 | password = "nixos"; 31 | extraGroups = [ "wheel" ]; 32 | }; 33 | ``` 34 | Adjust the username, password, and groups as you see fit. To change the timezone, locate the following lines: 35 | ``` 36 | # You can set your timezone here 37 | # time.timeZone = "Europe/Dublin"; 38 | ``` 39 | Uncomment the timezone option and insert your timezone. 40 | 41 | You can tune other options as well. Consult the [NixOS manual](https://nixos.org/manual/nixos/stable/options) for the list of possible options. 42 | Board-specific options are set in the files `orangepi5.nix` and `orangepi5plus.nix`. Notice, that the default network name differs between the boards. 43 | 4. Build the image 44 | 45 | On a `aarch64-linux` machine: 46 | ``` 47 | nix build .#nixosConfigurations.orangepi5.config.system.build.sdImage 48 | ``` 49 | to build an Orange Pi 5 image; or: 50 | ``` 51 | nix build .#nixosConfigurations.orangepi5plus.config.system.build.sdImage 52 | ``` 53 | to build an Orange Pi 5 Plus image. 54 | 55 | On a `x86_64-linux` machine: 56 | 57 | Cross compilation does not use the binary pachage cache and is prone to errors. It is recommended to use a `aarch64-linux` machine instead. 58 | 59 | Locate the following lines in `configuration.nix` 60 | ``` 61 | # to build on aarch64 machine 62 | hostPlatform = { system = "aarch64-linux"; }; 63 | 64 | # to build on x86_64 machine 65 | # hostPlatform = { system = "aarch64-linux"; }; 66 | # buildPlatform = { system = "x86_64-linux"; }; 67 | ``` 68 | and change it to 69 | ``` 70 | # to build on aarch64 machine 71 | # hostPlatform = { system = "aarch64-linux"; }; 72 | # to build on x86_64 machine 73 | hostPlatform = { system = "aarch64-linux"; }; 74 | buildPlatform = { system = "x86_64-linux"; }; 75 | ``` 76 | Build the image as described above. 77 | 6. Flash the image: 78 | ``` 79 | zstdcat result/sd-image/nixos-sd-image-23.11pre-git-aarch64-linux.img.zst | dd of=/dev/sdX bs=1M 80 | ``` 81 | Replace `/dev/sdX` with the device node corresponding to your SD card. 82 | 83 | ### Device Tree Overlays 84 | Device tree overlays can be enabled using the `board.hardware.enabled` option. Examples are provided in the flake templates. For a complete list of hardware options, refer to the board-specific modules in the source code. 85 | -------------------------------------------------------------------------------- /modules/util/generic-extlinux-compatible/extlinux-conf-builder.sh: -------------------------------------------------------------------------------- 1 | #! @bash@/bin/sh -e 2 | 3 | shopt -s nullglob 4 | 5 | export PATH=/empty 6 | for i in @path@; do PATH=$PATH:$i/bin; done 7 | 8 | usage() { 9 | echo "usage: $0 -t -c [-d ] [-g ] [-n ] [-r]" >&2 10 | exit 1 11 | } 12 | 13 | timeout= # Timeout in centiseconds 14 | default= # Default configuration 15 | target=/boot # Target directory 16 | numGenerations=0 # Number of other generations to include in the menu 17 | 18 | while getopts "t:c:d:g:n:r" opt; do 19 | case "$opt" in 20 | t) # U-Boot interprets '0' as infinite and negative as instant boot 21 | if [ "$OPTARG" -lt 0 ]; then 22 | timeout=0 23 | elif [ "$OPTARG" = 0 ]; then 24 | timeout=-10 25 | else 26 | timeout=$((OPTARG * 10)) 27 | fi 28 | ;; 29 | c) default="$OPTARG" ;; 30 | d) target="$OPTARG" ;; 31 | g) numGenerations="$OPTARG" ;; 32 | n) dtbName="$OPTARG" ;; 33 | r) noDeviceTree=1 ;; 34 | \?) usage ;; 35 | esac 36 | done 37 | 38 | [ "$timeout" = "" -o "$default" = "" ] && usage 39 | 40 | mkdir -p $target/nixos 41 | mkdir -p $target/extlinux 42 | 43 | # Convert a path to a file in the Nix store such as 44 | # /nix/store/-/file to --. 45 | cleanName() { 46 | local path="$1" 47 | echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g' 48 | } 49 | 50 | # Copy a file from the Nix store to $target/nixos. 51 | declare -A filesCopied 52 | 53 | copyToKernelsDir() { 54 | local src=$(readlink -f "$1") 55 | local dst="$target/nixos/$(cleanName $src)" 56 | # Don't copy the file if $dst already exists. This means that we 57 | # have to create $dst atomically to prevent partially copied 58 | # kernels or initrd if this script is ever interrupted. 59 | if ! test -e $dst; then 60 | local dstTmp=$dst.tmp.$$ 61 | cp -r $src $dstTmp 62 | mv $dstTmp $dst 63 | fi 64 | filesCopied[$dst]=1 65 | result=$dst 66 | } 67 | 68 | # Copy its kernel, initrd and dtbs to $target/nixos, and echo out an 69 | # extlinux menu entry 70 | addEntry() { 71 | local path=$(readlink -f "$1") 72 | local tag="$2" # Generation number or 'default' 73 | 74 | if ! test -e $path/kernel -a -e $path/initrd; then 75 | return 76 | fi 77 | 78 | copyToKernelsDir "$path/kernel"; kernel=$result 79 | copyToKernelsDir "$path/initrd"; initrd=$result 80 | dtbDir=$(readlink -m "$path/dtbs") 81 | if [ -e "$dtbDir" ]; then 82 | copyToKernelsDir "$dtbDir"; dtbs=$result 83 | fi 84 | 85 | timestampEpoch=$(stat -L -c '%Z' $path) 86 | 87 | timestamp=$(date "+%Y-%m-%d %H:%M" -d @$timestampEpoch) 88 | nixosLabel="$(cat $path/nixos-version)" 89 | extraParams="$(cat $path/kernel-params)" 90 | 91 | echo 92 | echo "LABEL nixos-$tag" 93 | if [ "$tag" = "default" ]; then 94 | echo " MENU LABEL NixOS - Default" 95 | else 96 | echo " MENU LABEL NixOS - Configuration $tag ($timestamp - $nixosLabel)" 97 | fi 98 | echo " LINUX ../nixos/$(basename $kernel)" 99 | echo " INITRD ../nixos/$(basename $initrd)" 100 | echo " APPEND init=$path/init $extraParams" 101 | 102 | if [ -n "$noDeviceTree" ]; then 103 | return 104 | fi 105 | 106 | if [ -f "$path/devicetree-overlays" ]; then 107 | local dtbOverlays="$(cat $path/devicetree-overlays)" 108 | fi 109 | 110 | if [ -d "$dtbDir" ]; then 111 | # if a dtbName was specified explicitly, use that, else use FDTDIR 112 | if [ -n "$dtbName" ]; then 113 | echo " FDT ../nixos/$(basename $dtbs)/${dtbName}" 114 | else 115 | echo " FDTDIR ../nixos/$(basename $dtbs)" 116 | fi 117 | 118 | if [ -n "$dtbOverlays" ]; then 119 | echo -n " FDTOVERLAYS" 120 | for overlay in $dtbOverlays; do 121 | echo -n " ../nixos/$(basename $dtbs)/${overlay}" 122 | done 123 | echo 124 | fi 125 | else 126 | if [ -n "$dtbName" ]; then 127 | echo "Explicitly requested dtbName $dtbName, but there's no FDTDIR - bailing out." >&2 128 | exit 1 129 | fi 130 | if [ -n "$dtbOverlays" ]; then 131 | echo "Requested overlays $dtbOverlays, but there's no FDTDIR - bailing out." >&2 132 | exit 1 133 | fi 134 | fi 135 | } 136 | 137 | tmpFile="$target/extlinux/extlinux.conf.tmp.$$" 138 | 139 | cat > $tmpFile <> $tmpFile 150 | 151 | if [ "$numGenerations" -gt 0 ]; then 152 | # Add up to $numGenerations generations of the system profile to the menu, 153 | # in reverse (most recent to least recent) order. 154 | for generation in $( 155 | (cd /nix/var/nix/profiles && ls -d system-*-link) \ 156 | | sed 's/system-\([0-9]\+\)-link/\1/' \ 157 | | sort -n -r \ 158 | | head -n $numGenerations); do 159 | link=/nix/var/nix/profiles/system-$generation-link 160 | addEntry $link $generation 161 | done >> $tmpFile 162 | fi 163 | 164 | mv -f $tmpFile $target/extlinux/extlinux.conf 165 | 166 | # Remove obsolete files from $target/nixos. 167 | for fn in $target/nixos/*; do 168 | if ! test "${filesCopied[$fn]}" = 1; then 169 | echo "Removing no longer needed boot file: $fn" 170 | chmod +w -- "$fn" 171 | rm -rf -- "$fn" 172 | fi 173 | done 174 | -------------------------------------------------------------------------------- /modules/default.nix: -------------------------------------------------------------------------------- 1 | self: rec { 2 | rk3588x-board = { lib, pkgs, config, ... }: let 3 | inherit ((import ./lib) { inherit lib; }) mkOverlayOption; 4 | mkRockchipOption = mkOverlayOption "rockchip/overlay"; 5 | 6 | boardCfg = config.board; 7 | in { 8 | imports = with self.inputs; [ 9 | "${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" 10 | ./util/fdt-overlays.nix 11 | ]; 12 | 13 | options.board = with lib; { 14 | name = mkOption { 15 | type = types.str; 16 | internal = true; 17 | readOnly = true; 18 | }; 19 | 20 | uBootPackage = mkOption { 21 | type = types.package; 22 | internal = true; 23 | readOnly = true; 24 | }; 25 | 26 | hardware = { 27 | 28 | available = mkOption { 29 | type = types.attrs; 30 | internal = true; 31 | readOnly = true; 32 | }; 33 | 34 | customOverlays = mkOption { 35 | type = types.attrsOf (types.submodule { 36 | options = { 37 | overlay = mkOption { 38 | type = types.str; 39 | description = "Name of the device tree overlay (without path or extension)."; 40 | }; 41 | inverse = mkOption { 42 | type = types.bool; 43 | default = false; 44 | description = "Whether the overlay is disabled when enabled (e.g., for disable-LED overlays)."; 45 | }; 46 | description = mkOption { 47 | type = types.str; 48 | default = ""; 49 | description = "Description of the custom overlay."; 50 | }; 51 | }; 52 | }); 53 | default = {}; 54 | description = '' 55 | Additional hardware overlays to make available for the board. 56 | These are merged with the predefined hardware.available overlays. 57 | Ensure the corresponding .dtbo files are included in hardware.firmware, 58 | e.g., by adding a package that installs them to /rockchip/overlay. 59 | ''; 60 | }; 61 | 62 | enabled = builtins.mapAttrs (_: args: mkRockchipOption args) 63 | # Merge predefined and custom overlays 64 | (lib.mergeAttrs boardCfg.hardware.available boardCfg.hardware.customOverlays); 65 | }; 66 | }; 67 | 68 | config = { 69 | assertions = lib.mapAttrsToList (name: _: { 70 | assertion = ! (lib.hasAttr name boardCfg.hardware.available) || (boardCfg.hardware.customOverlays.${name}.overlay == boardCfg.hardware.available.${name}.overlay); 71 | message = "Custom overlay '${name}' overrides a predefined overlay with a different configuration, which may be unintended."; 72 | }) boardCfg.hardware.customOverlays; 73 | 74 | boot = { 75 | supportedFilesystems = lib.mkForce [ 76 | "vfat" 77 | "fat32" 78 | "exfat" 79 | "ext4" 80 | "btrfs" 81 | ]; 82 | 83 | kernelParams = [ 84 | "console=ttyFIQ0,1500000n8" 85 | ]; 86 | 87 | initrd.includeDefaultModules = lib.mkForce false; 88 | initrd.availableKernelModules = lib.mkForce []; 89 | 90 | kernelPackages = pkgs.linuxPackagesFor self.packages.${pkgs.stdenv.buildPlatform.system}.linux-xunlong-rk35xx; 91 | }; 92 | 93 | sdImage = { 94 | firmwarePartitionOffset = 16; 95 | postBuildCommands = "dd if=${boardCfg.uBootPackage}/u-boot-rockchip.bin of=$img seek=64 conv=notrunc"; 96 | }; 97 | 98 | hardware = { 99 | deviceTree.enabledOverlays = with builtins; concatLists (attrValues boardCfg.hardware.enabled); 100 | graphics.enable = true; 101 | graphics.extraPackages = [ self.packages.${pkgs.stdenv.buildPlatform.system}.libmali-valhall-g610 ]; 102 | firmware = lib.mkForce (with pkgs; [ 103 | self.packages.${pkgs.stdenv.buildPlatform.system}.orangepi-firmware 104 | self.packages.${pkgs.stdenv.buildPlatform.system}.mali-firmware-g610 105 | ]); 106 | }; 107 | }; 108 | }; 109 | 110 | orangepi-5 = { pkgs, ... }: { 111 | imports = [ rk3588x-board ]; 112 | 113 | board = { 114 | name = "Orange Pi 5"; 115 | uBootPackage = pkgs.ubootOrangePi5; 116 | 117 | hardware.available = { 118 | led = { 119 | overlay = "rk3588-disable-led"; 120 | inverse = true; 121 | description = "Whether to disable LED"; 122 | }; 123 | 124 | can1-m1 = { overlay = "rk3588-can1-m1"; }; 125 | can2-m1 = { overlay = "rk3588-can2-m1"; }; 126 | 127 | dmc = { overlay = "rk3588-dmc"; }; 128 | 129 | i2c1-m2 = { overlay = "rk3588-i2c1-m2"; }; 130 | i2c1-m4 = { overlay = "rk3588-i2c1-m4"; }; 131 | i2c3-m0 = { overlay = "rk3588-i2c3-m0"; }; 132 | i2c5-m3 = { overlay = "rk3588-i2c5-m3"; }; 133 | 134 | lcd1 = { overlay = "rk3588-lcd1"; }; 135 | lcd2 = { overlay = "rk3588-lcd2"; }; 136 | 137 | ov13850-c1 = { overlay = "rk3588-ov13850-c1"; }; 138 | ov13850-c2 = { overlay = "rk3588-ov13850-c2"; }; 139 | ov13850-c3 = { overlay = "rk3588-ov13850-c3"; }; 140 | ov13855-c1 = { overlay = "rk3588-ov13855-c1"; }; 141 | ov13855-c2 = { overlay = "rk3588-ov13855-c2"; }; 142 | ov13855-c3 = { overlay = "rk3588-ov13855-c3"; }; 143 | 144 | pwm0-m1 = { overlay = "rk3588-pwm0-m1"; }; 145 | pwm1-m1 = { overlay = "rk3588-pwm1-m1"; }; 146 | pwm1-m2 = { overlay = "rk3588-pwm1-m2"; }; 147 | pwm3-m0 = { overlay = "rk3588-pwm3-m0"; }; 148 | pwm3-m2 = { overlay = "rk3588-pwm3-m2"; }; 149 | pwm13-m2 = { overlay = "rk3588-pwm13-m2"; }; 150 | pwm14-m1 = { overlay = "rk3588-pwm14-m1"; }; 151 | pwm15-m2 = { overlay = "rk3588-pwm15-m2"; }; 152 | 153 | spi4-m0-cs1-spidev = { overlay = "rk3588-spi4-m0-cs1-spidev"; }; 154 | 155 | ssd-sata0 = { overlay = "rk3588-ssd-sata0"; }; 156 | ssd-sata2 = { overlay = "rk3588-ssd-sata2"; }; 157 | 158 | uart0-m2 = { overlay = "rk3588-uart0-m2"; }; 159 | uart1-m1 = { overlay = "rk3588-uart1-m1"; }; 160 | uart3-m0 = { overlay = "rk3588-uart3-m0"; }; 161 | uart4-m0 = { overlay = "rk3588-uart4-m0"; }; 162 | 163 | wifi-ap6275p = { overlay = "rk3588-wifi-ap6275p"; }; 164 | wifi-pcie = { overlay = "rk3588-wifi-pcie"; }; 165 | }; 166 | }; 167 | }; 168 | 169 | orangepi-5-plus = { pkgs, ... }: { 170 | imports = [ rk3588x-board ]; 171 | 172 | board = { 173 | name = "Orange Pi 5 Plus"; 174 | uBootPackage = pkgs.ubootOrangePi5Plus; 175 | 176 | hardware.available = { 177 | leds = { 178 | overlay = "rk3588-opi5plus-disable-leds"; 179 | inverse = true; 180 | description = "Whether to disable LEDs"; 181 | }; 182 | 183 | can0-m0 = { overlay = "rk3588-can0-m0"; }; 184 | can1-m0 = { overlay = "rk3588-can1-m0"; }; 185 | 186 | dmc = { overlay = "rk3588-dmc"; }; 187 | 188 | gc5035 = { overlay = "rk3588-opi5plus-gc5035"; }; 189 | 190 | hdmi2-8k = { overlay = "rk3588-hdmi2-8k"; }; 191 | hdmirx = { overlay = "rk3588-hdmirx"; }; 192 | 193 | i2c2-m0 = { overlay = "rk3588-i2c2-m0"; }; 194 | i2c2-m4 = { overlay = "rk3588-i2c2-m4"; }; 195 | i2c4-m3 = { overlay = "rk3588-i2c4-m3"; }; 196 | i2c5-m3 = { overlay = "rk3588-i2c5-m3"; }; 197 | i2c8-m2 = { overlay = "rk3588-i2c8-m2"; }; 198 | 199 | lcd = { overlay = "rk3588-opi5plus-lcd"; }; 200 | 201 | ov13850 = { overlay = "rk3588-opi5plus-ov13850"; }; 202 | ov13855 = { overlay = "rk3588-opi5plus-ov13855"; }; 203 | 204 | pwm0-m0 = { overlay = "rk3588-pwm0-m0"; }; 205 | pwm0-m2 = { overlay = "rk3588-pwm0-m2"; }; 206 | pwm1-m0 = { overlay = "rk3588-pwm1-m0"; }; 207 | pwm1-m2 = { overlay = "rk3588-pwm1-m2"; }; 208 | pwm11-m0 = { overlay = "rk3588-pwm11-m0"; }; 209 | pwm12-m0 = { overlay = "rk3588-pwm12-m0"; }; 210 | pwm13-m0 = { overlay = "rk3588-pwm13-m0"; }; 211 | pwm14-m0 = { overlay = "rk3588-pwm14-m0"; }; 212 | pwm14-m2 = { overlay = "rk3588-pwm14-m2"; }; 213 | 214 | spi0-m2-cs0-cs1-spidev = { overlay = "rk3588-spi0-m2-cs0-cs1-spidev"; }; 215 | spi0-m2-cs0-spidev = { overlay = "rk3588-spi0-m2-cs0-spidev"; }; 216 | spi0-m2-cs1-spidev = { overlay = "rk3588-spi0-m2-cs1-spidev"; }; 217 | spi4-m1-cs0-cs1-spidev = { overlay = "rk3588-spi4-m1-cs0-cs1-spidev"; }; 218 | spi4-m1-cs0-spidev = { overlay = "rk3588-spi4-m1-cs0-spidev"; }; 219 | spi4-m1-cs1-spidev = { overlay = "rk3588-spi4-m1-cs1-spidev"; }; 220 | spi4-m2-cs0-spidev = { overlay = "rk3588-spi4-m2-cs0-spidev"; }; 221 | 222 | ssd-sata0 = { overlay = "rk3588-ssd-sata0"; }; 223 | ssd-sata2 = { overlay = "rk3588-ssd-sata2"; }; 224 | 225 | uart1-m1 = { overlay = "rk3588-uart1-m1"; }; 226 | uart3-m1 = { overlay = "rk3588-uart3-m1"; }; 227 | uart4-m2 = { overlay = "rk3588-uart4-m2"; }; 228 | uart6-m1 = { overlay = "rk3588-uart6-m1"; }; 229 | uart7-m2 = { overlay = "rk3588-uart7-m2"; }; 230 | uart8-m1 = { overlay = "rk3588-uart8-m1"; }; 231 | 232 | wifi-ap6275p = { overlay = "rk3588-wifi-ap6275p"; }; 233 | wifi-pcie = { overlay = "rk3588-wifi-pcie"; }; 234 | }; 235 | }; 236 | }; 237 | } 238 | --------------------------------------------------------------------------------