NixOS for AML-S905X-CC Le Potato

Hello! First of all I realize this isn’t an officially supported distro. Even so, would you be able to help me perhaps troubleshoot this sd card image build, and maybe consider supporting NixOS with an official guide?

In any case, here’s my really basic image

{ server, device_id, device_token }:
let
  nixpkgs_path = <nixpkgs>;
  build_system = (configuration:
    let
      eval = import
        (nixpkgs_path + /nixos/lib/eval-config.nix)
        {
          system = builtins.currentSystem;
          modules = [ configuration ];
        };
    in
    {
      inherit (eval) pkgs config options;
      system = eval.config.system.build.toplevel;
      inherit (eval.config.system.build) vm vmWithBootLoader;
    });
in
build_system
  ({ config, modulesPath, pkgs, lib, ... }:
  {
    imports = [
      (nixpkgs_path + /nixos/modules/profiles/base.nix)
      (nixpkgs_path + /nixos/modules/installer/sd-card/sd-image.nix)

      # libretech-cc (le potato) sd image customizations
      ({ config, lib, pkgs, ... }:
        {
          boot.loader.grub.enable = false;
          boot.loader.generic-extlinux-compatible.enable = true;

          boot.consoleLogLevel = lib.mkDefault 7;

          # The serial ports listed here are:
          # - ttyS0: for Tegra (Jetson TX1)
          # - ttyAMA0: for QEMU's -machine virt
          boot.kernelParams = [ "console=ttyS0,115200n8" "console=ttyAMA0,115200n8" "console=tty0" ];

          sdImage = {
            populateFirmwareCommands =
              let
                configTxt = pkgs.writeText "config.txt" ''
                  [potato]
                  kernel=u-boot.bin

                  [all]
                  # Boot in 64-bit mode.
                  arm_64bit=1

                  # U-Boot needs this to work, regardless of whether UART is actually used or not.
                  # Look in arch/arm/mach-bcm283x/Kconfig in the U-Boot tree to see if this is still
                  # a requirement in the future.
                  enable_uart=1

                  # Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
                  # when attempting to show low-voltage or overtemperature warnings.
                  avoid_warnings=1
                '';
              in
              ''
                (cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/)

                # Add the config
                cp ${configTxt} firmware/config.txt

                cp ${pkgs.ubootLibreTechCC}/u-boot.bin firmware/
              '';
            populateRootCommands = ''
              mkdir -p ./files/boot
              ${config.boot.loader.generic-extlinux-compatible.populateCmd} -c ${config.system.build.toplevel} -d ./files/boot
            '';
            postBuildCommands = ''
              dd if=\${pkgs.ubootLibreTechCC}/u-boot.bin of=$img bs=512 seek=1 conv=notrunc
            '';
          };
        })

      # System def
      ({ config, modulesPath, pkgs, lib, ... }: {
        config = {
          nixpkgs = {
            crossSystem.system = "aarch64-linux";
          };
          sdImage = {
            imageName = "device.img";
            compressImage = false;
          };
        };
      })
    ];
  })

The system itself has basically nothing. I think it’d come up with a login screen, and root with a default password, but that’s it (no software installed, extra services, etc).

The imported sd-image.nix file tells it to generate an sd card image. It creates a FAT32 partition at 8MB in with config.txt and (theoretically) firmware. There’s a sd-image-aarch64.nix file which specializes sd-image.nix which is pretty raspberry-pi specific, and I based mine off that - I left the start*.elf stuff because I don’t know what it’s for and it didn’t say it was specific to a board. This says that Le Potato has no firmware, so maybe none is needed.

pkgs.ubootLibreTechCC builds u-boot with that config. The postBuildCommands for the sd image dd’s the u-boot at the 512B offset of the image.

I build it with (device.nix = the text above)

nix-build device.nix -A config.system.build.sdImage -o out

This produces a .img file in out/sd-image/device.img, which I copy to the sd card with cp out/sd-image/device.img /dev/sdd.

The image doesn’t boot (just the red and blue lights, no green, no hdmi output).

So just throwing some questions out there:

  • Did I miss something obvious?
  • Does the green light turn on when it finds the bootloader, or later?
  • Does this actually need firmware? Maybe it needs dtb files and that’s why the light isn’t turning on.
  • Does this need a UEFI partition? The official NixOS installer sd card image doesn’t have UEFI so I assume the discussion of it using that for boot is due to Librecomputer’s u-boot build specifically.
  • Does Le Potato support UEFI? I couldn’t get it to boot with a UEFI iso, and while this isn’t explicit, it suggests that aml-s905x-cc is non-UEFI hence the specific image. This contradicts the amazon product page which says it supports UEFI so I’m not sure.

Edit: Also, is there a qemu-system-aarch64 invocation I could use to test the image and maybe get better diagnostics? The issues may be too low level for qemu, I think it skips part of the boot.

Just found Build sd-card image for LePotato · josqu4red/nix-config@83e317e · GitHub from the uboot package author. Main difference appears to be what uboot binary is copied over (and an additional file).

I’ll try it tomorrow.

The bootloader should be copied from our boot server. http://boot.libre.computer/ci/MODEL

Building your own bootloader is not recommended. There are a lot of optimizations beyond the upstream u-boot that we maintain since they cannot go upstream.

Why can’t they go upstream? If they’re just optimizations it seems like a fair choice for the user to go with upstream if they wish.

Anyways, it worked! Here for posterity, from the linked commit (in case it disappears):

{ config, lib, pkgs, ... }: {
              imports = [
                "${inputs.nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix"
              ];
              sdImage = {
                compressImage = false;
                populateFirmwareCommands = "";
                postBuildCommands = ''
                  dd if=${pkgs.ubootLibreTechCC}/u-boot.gxl.sd.bin of=$img conv=fsync,notrunc bs=512 seek=1 skip=1
                  dd if=${pkgs.ubootLibreTechCC}/u-boot.gxl.sd.bin of=$img conv=fsync,notrunc bs=1 count=444
                '';
              };
              nixpkgs = {
                config.allowUnfree = true;
                localSystem.system = "x86_64-linux";
                crossSystem.system = "aarch64-linux";
              };
              system.stateVersion = "22.05";
            }

You don’t need the -aarch64.nix thing as long as you copy over one property, but maybe it’ll be less rpi specific in the future.

So to summarize, AML-S905X-CC

  1. Doesn’t need any firmware, dtb, etc. I don’t know how this works, but :person_shrugging:
  2. Doesn’t support uefi. You need u-boot
  3. Nix-upstream has a AML-S905X-CC uboot configuration pre-packaged
  4. uboot needs to be pasted into the image (.gxl.sd.bin, not .bin) as above
  5. Nothing else is needed (ex: no grub)

This is not necessary for AML-S905X-CC. It was incorrectly carried over from ODROID-C2.

1 Like

Ah excellent, thanks! So the dxl.sd.bin at 512 byte offset is still required, just not the 444 byte wipe. :heavy_check_mark: