Close

Building Upstream Firmare with Nix

A project log for Blue Pill to Black Magic Probe

Turning the Blue Pill development board into a Black Magic Probe

marblemarble 09/03/2022 at 18:120 Comments

Thankfully, nixpkgs already contain blackmagic as a package, which also already builds firmware images. So all we need to do to build these is run 

nix-build '<nixpkgs>' -A blackmagic

This gives us a result directory, which is a symbolic link to the built package. Since '<nixpkgs>' means the build is based on the system nix channel, chances are that the pre-built package is fetched from a build server.

This also means that we get the Black Magic version that is pinned in the system channel package description

[...]
  firmwareVersion = "v1.7.1-415-gc4869a5";

  src = fetchFromGitHub {
    owner = "blacksphere";
    repo = "blackmagic";
    rev = "c4869a54733ae92099a7316954e34d1ab7b6097c";
    hash = "sha256-0MC1v/5u/txSshxkOI5TErMRRrYCcHly3qbVTAk9Vc0=";
    fetchSubmodules = true;
  };
[...]

If we now wanted to build a different version of Black Magic, we can start by writing a nix-expression that essentially does the exact same thin we were doing in the command line.

{ pkgs ? import <nixpkgs> { }
, ...
}:

with pkgs;

blackmagic

This expression defines a function that takes an attribute set as the parameter and returns the blackmagic package. The only attribute in the parameter is the package repository, which defaults to importing the system package channel. The with pkgs; part is syntax sugar letting nix automatically search in pkgs for names that are not defined in the default environment, like just blackmagic is this case. The alternative would be writing pkgs.blackmagic .

The problem with this is, that the system channel is not fixed, which means that this expression is dependent on an external state and thus not reproducible.

The fix for that is importing a pinned version of the nixpkgs repository.

{ pkgs ? import
    (builtins.fetchGit {
      name = "nixos-22.05-2022_08_27";
      url = "https://github.com/nixos/nixpkgs/";
      ref = "refs/heads/nixos-22.05";
      rev = "f11e12ac6af528c1ba12426ce83cee26f21ceafd";
    })
    { }
, ...
}:
[...]

The value in rev can be obtained by running

git ls-remote https://github.com/nixos/nixpkgs/ nixos-22.05

We now can make changes to the blackmagic by using the overrideAttrs function. The first thing we can do is change the version to the current Black Magic main branch.

[...]
blackmagic.overrideAttrs (oldAttrs: rec {
  version = "unstable-2022-09-01";
  firmwareVersion = "v1.8.0-639-g3e8f296d";
  src = fetchFromGitHub {
    owner = "blacksphere";
    repo = "blackmagic";
    rev = "3e8f296dd59a1edbbd666c534ae4c046cd369d54";
    sha256 = "0vh0359ya3ajslk0h9i3qz37b9wc7ww63yxy2h08ngghnj6xq82i";
    fetchSubmodules = true;
  };
})

The sha256 hash is obtained by running nix-buil with a wrong hash first and then using the reported actual hash.

The first problem during the build is that version.h is missing. This is due to a breaking change in the Black Magic repository which demand that we make changes to the build.

[...]
  postPatch = oldAttrs.postPatch + ''
    # Prevent calling out to `git' to generate a version number:
    substituteInPlace src/Makefile \
      --replace '$(shell git describe --always --dirty --tags)' '${firmwareVersion}'
  '';
[...]

The next problem is that the common platform is not a proper target, but contains a Makefile.inc. This confuses the build script. One solution is to copy and fix the build script.

[...]
for platform in platforms/*/platform.h; do
  probe=$(basename "$(dirname "$platform")")
  make_platform "$probe"
done

This also requires us to state the usage of the script in our override, which is the exact same code as in the package, but will reference our helper.sh instead due to file loation. If you wanted more clarity, you could use a different name.

[...]
  buildPhase = ''
    runHook preBuild
    ${stdenv.shell} ${./helper.sh}
    runHook postBuild
  '';
[...]

When now running nix-build we get the result symlink again, containing the new package contents.

Discussions