Notes on getting NixOS on the Radxa Dragon Q6A

The Radxa Dragon Q6A is a quite capable SBC specially for the price I've paid for it.

As of the time of this writing the latest-nixos-minimal-aarch64-linux.iso fails to boot from USB and loading from the SD-CARD results in a very slow environment. In this setup I'll be installing NixOS on the NVME drive and haven't tested UFS2. Also please take into consideration Radxa is actively working and submiting patches for the platform so by the time you are reading these instructions might be outdated.

Installation steps

So first we need to download the latest version of Radxa OS and use it to update the firmware to at least version 260120.

If you don't have or don't want to connect a display and keyboard UART works fine including the EFI BIOS menu. Make sure to check the Radxa documentation on which pins to connect. If you are using the compatible PoE HAT adapter be mindful it doesn't have passthrough GPIO pins.

You also need to make sure virtualization is disabled on the UEFI firmware otherwise /dev/mtd0 won't be available to flash the SPI. You can enable it later for KVM support.

On Radxa OS (username: radxa, password: radxa) make sure to disable automatic suspend on preferences as it doesn't take into account SSH sessions when suspending the system and some tasks like compiling a kernel might take a long time.

Start by running sudo rsetup and navigating to System → Bootloader Management → Update SPI Bootloader. Select radxa-dragon-q6a and run the scary steps. If rsetup complains about unsupported or missing device and there is no /dev/mtd0 device then you are running with virtualization enabled so you need to go back to the UEFI settings and disable it.

During the process I've observed "error 13 (permission denied)" while trying to erase MTD before it starts to slowly erase and write to the device but those errors don't seem to have influenced the results and the message "The SPI bootloader has been updated successfully" should confirm the update happened.

Reboot and enter the firmware setup to verify you are running 260120. You can also enable virtualization now if you want to.

Back to Radxa OS install Nix using the instructions on https://nixos.org/download/#nix-install-linux for generic Linux:

sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon

From now on you will follow the instructions described in https://nixos.org/manual/nixos/stable/#sec-installing-from-other-distro to run a NixOS installation environment on top of Radxa OS. Target system should be UEFI. The important parts are config.boot and config.hardware but you must also disable TPM:

boot = {
  kernelPackages = lib.mkDefault (
    pkgs.linuxPackagesFor (import ./modules/kernels/dragon.nix { inherit lib pkgs; })
  );
  kernelParams = [
    "console=ttyMSM0,115200n8"
    "earlycon"
    "keep_bootcon"
  ];
  loader = {
    systemd-boot = {
      enable = true;
      installDeviceTree = true;
    };
    efi = {
      canTouchEfiVariables = false;
    };
  };
  initrd = {
    kernelModules = [
      "usb_storage"
      "nvme"
      "xhci_hcd"
    ];
    systemd = {
      tpm2 = {
        enable = false;
      };
    };
  };
};
systemd = {
  tpm2 = {
    enable = false;
  };
};

MESA has some open source support for Adreno GPU so you can include hardware.graphics without any special configurations.

hardware = {
  graphics = {
    enable = true;
  };
  firmware = with pkgs; [
    linux-firmware
  ];
  deviceTree = {
    enable = true;
    name = "qcom/qcs6490-radxa-dragon-q6a.dtb";
  };
};

Custom kernel module (./modules/kernels/dragon.nix)

Extracted from Cryolitia-Forks/nixos-hardware/tree/q6a

{
  lib,
  pkgs,
  ...
}:
pkgs.buildLinux {
  defconfig = "qcom_module_defconfig";
  version = "6.18.2-1-q6a-radxa";
  modDirVersion = "6.18.2";

  src = pkgs.fetchFromGitHub {
    owner = "radxa";
    repo = "kernel";
    # Use the same kernel commit ID as
    # https://github.com/radxa-pkg/linux-qcom/tree/6.18.2-1
    rev = "ca7680ac08f031ed0a952dbc6174ced3d513bef9";
    hash = "sha256-xCgW/2iaWu9JWz3LLeV/xwzvtx5JComcPZpgH7rnOgw=";
  };

  structuredExtraConfig = with lib.kernel; {
    EFI_ZBOOT = lib.mkForce no;
    NVME_AUTH = lib.mkForce yes;
  };

  extraConfig = ''
    COMPRESSED_INSTALL n
    DRM_NOVA n
    NOVA_CORE n
    WLAN_VENDOR_AIC8800 n
  '';

  ignoreConfigErrors = true;
}

Compiling the kernel on the device might take a long time. You might consider using an external builder specially if you have a powerful ARM64 machine such as Apple Silicon.

Tip: If you see an error complaining about the mount command not found a workaround is to create a symlink ln -sf /nix/var/nix/profiles/system/sw/bin /mnt/sbin to satisfy the installer. You can remove it later.

What doesn't work?

Some useful outputs

Power draw

Taken from the PoE switch metrics. The graph includes both idle and a few hours under load compiling the Linux kernel.

/proc/cpuinfo

processor	: 0
processor	: 0
BogoMIPS	: 38.40
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x2
CPU part	: 0xd05
CPU revision	: 0

processor	: 1
BogoMIPS	: 38.40
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x2
CPU part	: 0xd05
CPU revision	: 0

processor	: 2
BogoMIPS	: 38.40
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x2
CPU part	: 0xd05
CPU revision	: 0

processor	: 3
BogoMIPS	: 38.40
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x2
CPU part	: 0xd05
CPU revision	: 0

processor	: 4
BogoMIPS	: 38.40
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0xd41
CPU revision	: 1

processor	: 5
BogoMIPS	: 38.40
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0xd41
CPU revision	: 1

processor	: 6
BogoMIPS	: 38.40
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0xd41
CPU revision	: 1

processor	: 7
BogoMIPS	: 38.40
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp
CPU implementer	: 0x41
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0xd41
CPU revision	: 1
    

ethtool


Settings for enp1s0:
	Supported ports: [ TP	 MII ]
	Supported link modes:   10baseT/Half 10baseT/Full
	                        100baseT/Half 100baseT/Full
	                        1000baseT/Full
	Supported pause frame use: Symmetric Receive-only
	Supports auto-negotiation: Yes
	Supported FEC modes: Not reported
	Advertised link modes:  10baseT/Half 10baseT/Full
	                        100baseT/Half 100baseT/Full
	                        1000baseT/Full
	Advertised pause frame use: Symmetric Receive-only
	Advertised auto-negotiation: Yes
	Advertised FEC modes: Not reported
	Link partner advertised link modes:  100baseT/Half 100baseT/Full
	                                     1000baseT/Full
	Link partner advertised pause frame use: Symmetric
	Link partner advertised auto-negotiation: Yes
	Link partner advertised FEC modes: Not reported
	Speed: 1000Mb/s
	Duplex: Full
	Auto-negotiation: on
	master-slave cfg: preferred slave
	master-slave status: master
	Port: Twisted Pair
	PHYAD: 0
	Transceiver: external
	MDI-X: Unknown
	Supports Wake-on: pumbg
	Wake-on: d
	Link detected: yes
 	  

lspci


# The last device is my aftermarket NVMe.
0000:00:00.0 PCI bridge: Qualcomm Technologies, Inc SM8250 PCIe Root Complex [Snapdragon 865/870 5G]
0000:01:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8211/8411 PCI Express Gigabit Ethernet Controller (rev 1b)
0001:00:00.0 PCI bridge: Qualcomm Technologies, Inc SM8250 PCIe Root Complex [Snapdragon 865/870 5G]
0001:01:00.0 Non-Volatile memory controller: SK hynix Gold P31/BC711/PC711 NVMe Solid State Drive

dmesg

dragonq6a_dmesg.txt

cryptsetup benchmark


# Tests are approximate using memory only (no storage IO).
PBKDF2-sha1      2766691 iterations per second for 256-bit key
PBKDF2-sha256    5363560 iterations per second for 256-bit key
PBKDF2-sha512    1696724 iterations per second for 256-bit key
PBKDF2-ripemd160  985503 iterations per second for 256-bit key
PBKDF2-whirlpool  496484 iterations per second for 256-bit key
argon2i       4 iterations, 910471 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
argon2id      4 iterations, 899956 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
#     Algorithm |       Key |      Encryption |      Decryption
        aes-cbc        128b      1211.6 MiB/s      2550.4 MiB/s
    serpent-cbc        128b        89.8 MiB/s       100.3 MiB/s
    twofish-cbc        128b       141.8 MiB/s       145.3 MiB/s
        aes-cbc        256b       972.3 MiB/s      2287.4 MiB/s
    serpent-cbc        256b        89.8 MiB/s       100.4 MiB/s
    twofish-cbc        256b       142.0 MiB/s       145.4 MiB/s
        aes-xts        256b      1907.0 MiB/s      1884.3 MiB/s
    serpent-xts        256b        92.7 MiB/s       102.4 MiB/s
    twofish-xts        256b       152.5 MiB/s       149.9 MiB/s
        aes-xts        512b      1710.4 MiB/s      1696.0 MiB/s
    serpent-xts        512b        92.8 MiB/s       102.7 MiB/s
    twofish-xts        512b       152.8 MiB/s       150.5 MiB/s

Notes on NPU

The FastRPC devices that are used to communicate with the NPU are detected:


crw------- 10,264 root 26 Apr 19:40 /dev/fastrpc-adsp
crw------- 10,263 root 26 Apr 19:40 /dev/fastrpc-cdsp
crw------- 10,262 root 26 Apr 19:40 /dev/fastrpc-cdsp-securei

However my attempts at building llama-cpp for Hexagon core dumps after attempting to establish a connection to the device (full error log included in original notes).


ggml-hex: Loading driver libcdsprpc.so
ggml-hex: FastRPC capability query failed (err -1)
ggml-hex: failed to query HTP version (err -1) defaulting to v73
ggml-hex: Hexagon backend (experimental) : allocating new registry : ndev 1
ggml-hex: Hexagon Arch version v73
ggml-hex: failed to get URI for session 0 : error 0xffffffff. Falling back to single session URI: file:///libggml-htp-v73.so?htp_iface_skel_handle_invoke&_modver=1.0&_dom=cdsp
ggml-hex: failed to enable unsigned PD for session 0 : error 0xffffffff
ggml-hex: releasing session: HTP0
ggml-hex: failed to create device/session 0
/build/ggml/src/ggml-backend.cpp:584: GGML_ASSERT(device) failed
/opt/llama.cpp/lib/libggml-base.so.0(+0xfa90)[0xffffab31fa90]
/opt/llama.cpp/lib/libggml-base.so.0(ggml_print_backtrace+0x244)[0xffffab31fa6c]
/opt/llama.cpp/lib/libggml-base.so.0(ggml_abort+0xe0)[0xffffab31eca0]
/opt/llama.cpp/lib/libggml-base.so.0(ggml_backend_dev_get_props+0x0)[0xffffab33ae58]
/opt/llama.cpp/lib/libllama-common.so.0(+0xc982c)[0xffffab9c982c]
/opt/llama.cpp/lib/libllama-common.so.0(+0x102b14)[0xffffaba02b14]
/opt/llama.cpp/lib/libllama-common.so.0(+0xfaf60)[0xffffab9faf60]
/opt/llama.cpp/lib/libllama-common.so.0(_Z19common_params_parseiPPcR13common_params13llama_examplePFviS0_E+0x1840)[0xffffab9f9cc4]
llama-server(+0x1dd10)[0xaaaacdcddd10]
/lib/aarch64-linux-gnu/libc.so.6(+0x284c4)[0xffffaadf84c4]
/lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0x98)[0xffffaadf8598]
llama-server(+0x1b870)[0xaaaacdcdb870]
Aborted (core dumped)
Positive note: Incidentally using Vulkan with Qwen_Qwen3.5-2B-Q8_0.gguf I am able to achieve 5.16 t/s on simple prompts.