diff --git a/flake.nix b/flake.nix index 0182671..04acb5a 100644 --- a/flake.nix +++ b/flake.nix @@ -185,6 +185,7 @@ microsoft-surface-laptop-amd = import ./microsoft/surface/surface-laptop-amd; microsoft-surface-common = import ./microsoft/surface/common; microsoft-surface-pro-3 = import ./microsoft/surface-pro/3; + milkv-pioneer = import ./milkv/pioneer; morefine-m600 = import ./morefine/m600; msi-b350-tomahawk = import ./msi/b350-tomahawk; msi-b550-a-pro = import ./msi/b550-a-pro; diff --git a/milkv/pioneer/README.md b/milkv/pioneer/README.md new file mode 100644 index 0000000..625c999 --- /dev/null +++ b/milkv/pioneer/README.md @@ -0,0 +1,50 @@ +# Creating an installation SD card image + +Create and customize a `flake.nix` file: + +```nix +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + nixos-hardware.url = "github:nixos/nixos-hardware"; + }; + + outputs = { nixpkgs, nixos-hardware, ... }: + let + supportedSystems = [ + "x86_64-linux" + "aarch64-linux" + "riscv64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + forAllSupportedSystems = nixpkgs.lib.genAttrs supportedSystems; + in + { + packages = forAllSupportedSystems (system: rec { + default = sd-image; + sd-image = (import "${nixpkgs}/nixos" { + configuration = { + imports = [ + "${nixos-hardware}/milkv/pioneer/sd-image-installer.nix" + ]; + + nixpkgs.buildPlatform.system = system; + nixpkgs.hostPlatform.system = "riscv64-linux"; + + system.stateVersion = "24.05"; + }; + inherit system; + }).config.system.build.sdImage; + }); + }; +} +``` + +Then build the image by running `nix build .#` in the same folder. + +# Known issues + +LinuxBoot will not output the boot menu on the serial console, only on the graphical console. +Unfortuately, it might also pick up boot options from other devices, e.g. an nvme or sata drive. +It might end up booting by default from those instead of booting from the SD card. diff --git a/milkv/pioneer/default.nix b/milkv/pioneer/default.nix new file mode 100644 index 0000000..aaf685a --- /dev/null +++ b/milkv/pioneer/default.nix @@ -0,0 +1,28 @@ +{ config, lib, pkgs, ... }: + +{ + boot = { + consoleLogLevel = lib.mkDefault 7; + initrd = { + availableKernelModules = [ + "amdgpu" + "radeon" + "mmc_block" + "sdhci_sophgo" + ]; + }; + kernelPackages = pkgs.linuxPackagesFor (pkgs.callPackage ./linux.nix { + inherit (config.boot) kernelPatches; + }); + kernelParams = lib.mkDefault [ + "earlycon" + "console=ttyS0,115200" + "console=tty1" + ]; + }; + + hardware.deviceTree = { + enable = true; + name = lib.mkDefault "sophgo/mango-milkv-pioneer.dtb"; + }; +} diff --git a/milkv/pioneer/fip.nix b/milkv/pioneer/fip.nix new file mode 100644 index 0000000..d4fb2a6 --- /dev/null +++ b/milkv/pioneer/fip.nix @@ -0,0 +1,7 @@ +{ fetchurl, ... }: + +# Download the vendor's Firmware Image Package +fetchurl { + url = "https://github.com/sophgo/bootloader-riscv/raw/3f750677e0249ff549ad3fe20bbc800998503539/firmware/fip.bin"; + hash = "sha256-rav00Ok6+FU77lI0piQPHCaz7Tw1RSbyUal4PyeSccg="; +} diff --git a/milkv/pioneer/firmware.nix b/milkv/pioneer/firmware.nix new file mode 100644 index 0000000..fb49962 --- /dev/null +++ b/milkv/pioneer/firmware.nix @@ -0,0 +1,46 @@ +{ stdenv +, writeText +, opensbi +, fip +, zsbl +, linuxboot-kernel +, linuxboot-initrd +, dtbs ? "${linuxboot-kernel}/dtbs" +, ... +}: + +let + # Configure a conf.init for linuxboot. If this is not found on the sdcard, + # zsbl will load it from spi flash even when booting from sd. That conf.ini + # might be configured differently and thus not properly boot from sd. + conf-ini = writeText "conf.ini" '' + [sophgo-config] + + [devicetree] + name = mango-milkv-pioneer.dtb + + [kernel] + name = riscv64_Image + + [firmware] + name = fw_dynamic.bin + + [ramfs] + name = initrd.img + + [eof] + ''; +in + +stdenv.mkDerivation { + name = "milkv-pioneer-firmware"; + buildCommand = '' + install -D ${conf-ini} $out/riscv64/conf.ini + install -D ${fip} $out/fip.bin + install -D ${zsbl} $out/zsbl.bin + install -D ${opensbi}/share/opensbi/lp64/generic/firmware/fw_dynamic.bin $out/riscv64/ + install -D ${linuxboot-initrd}/initrd.img $out/riscv64/ + install -D ${dtbs}/sophgo/mango-milkv-pioneer.dtb $out/riscv64/ + install -D ${linuxboot-kernel}/Image $out/riscv64/riscv64_Image + ''; +} diff --git a/milkv/pioneer/linux.nix b/milkv/pioneer/linux.nix new file mode 100644 index 0000000..b2396c7 --- /dev/null +++ b/milkv/pioneer/linux.nix @@ -0,0 +1,44 @@ +{ buildLinux, fetchFromGitHub, kernelPatches, lib, ... } @ args: + +let + modDirVersion = "6.6.20"; +in +buildLinux (args // { + inherit kernelPatches modDirVersion; + version = "${modDirVersion}-milkv-pioneer"; + src = fetchFromGitHub { + owner = "sophgo"; + repo = "linux-riscv"; + rev = "caa949e3690fe8a4656313b2b56f52666fa880db"; + hash = "sha256-qJpR3KMgvP4tfPfBfQ/MiEWg/uuuxHYuACK8taKKK3E="; + }; + + defconfig = "sophgo_mango_normal_defconfig"; + structuredExtraConfig = let inherit (lib.kernel) freeform module yes; in { + # LinuxBoot will override the console bootparams which will result + # in the distro kernel to be booted with e.g. console=tty1 only. + # https://github.com/sophgo/bootloader-riscv/issues/71 + # Force output on serial console through the config. This is also + # needed to get the forced serial-getty to be started. + # We also list tty1 again because according to + # https://docs.kernel.org/admin-guide/serial-console.html and + # https://0pointer.de/blog/projects/serial-console.html + # this will be the main console. + CMDLINE = freeform "console=ttyS0,115200 console=tty1"; + CMDLINE_EXTEND = yes; + + # Enable these explicitly because they are not enabled by the defconfig. + # The all-hardware profile expects these to be built. + VIRTIO_MENU = yes; + VIRTIO_PCI = module; + + # There is an i2c mcu driver (drivers/soc/sophgo/umcu) which is always + # compiled into the kernel. Hence some of the i2c support also needs to + # be compiled in instead of being compiled as a module. + I2C = yes; + I2C_CHARDEV = yes; + I2C_DESIGNWARE_PLATFORM = yes; + }; + + extraMeta.branch = "sg2042-dev-6.6"; +} // (args.argsOverride or { })) diff --git a/milkv/pioneer/linuxboot-initrd.nix b/milkv/pioneer/linuxboot-initrd.nix new file mode 100644 index 0000000..507fc0e --- /dev/null +++ b/milkv/pioneer/linuxboot-initrd.nix @@ -0,0 +1,73 @@ +{ buildPackages +, fetchFromGitHub +, fetchpatch +, linux-firmware +, buildGoModule +, ... +}: + +# Based on +# https://github.com/sophgo/bootloader-riscv/blob/e0839852d571df106db622611f4786ae17e8df0f/scripts/envsetup.sh#L809-L819 +let + u-root = buildPackages.buildGoModule rec { + pname = "u-root"; + version = "0.14.0"; + src = fetchFromGitHub { + owner = "u-root"; + repo = "u-root"; + rev = "v${version}"; + hash = "sha256-8zA3pHf45MdUcq/MA/mf0KCTxB1viHieU/oigYwIPgo="; + }; + vendorHash = null; + patches = [ + (fetchpatch { + url = "https://github.com/sophgo/bootloader-riscv/commit/322c3305763872a9b88a1c85d79bca63b8fbe7a6.patch"; + hash = "sha256-l5r3DbcMqRYD5FhRBqtEIEscZAdDvjmQJE4BIAtWYWE="; + stripLen = 1; + }) + ]; + + postInstall = '' + cp -r . $out/src + ''; + + # We only build the u-root binary in the build phase and the initrd in the + # postBuild hook. + subPackages = [ "." ]; + + # Tests time out after 10min for native riscv64 builds on the pioneer. + doCheck = false; + }; +in +buildGoModule { + name = "linuxboot-initrd"; + src = null; + vendorHash = null; + dontUnpack = true; + nativeBuildInputs = [ u-root ]; + + buildPhase = '' + runHook preBuild + pushd ${u-root}/src + mkdir -p $out + GOROOT="$(go env GOROOT)" u-root \ + -build bb \ + -uinitcmd=boot \ + -files "${linux-firmware}/lib/firmware/amdgpu/:lib/firmware/amdgpu/" \ + -files "${linux-firmware}/lib/firmware/radeon/:lib/firmware/radeon/" \ + -o $out/initramfs.cpio \ + core boot + popd + + # The vendor does not compress the initrd. We do since we include more + # firmware files. CRC32 is required by the kernel's decompressor. + xz --check=crc32 $out/initramfs.cpio + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mv $out/initramfs.cpio.xz $out/initrd.img + runHook postInstall + ''; +} diff --git a/milkv/pioneer/linuxboot-kernel.nix b/milkv/pioneer/linuxboot-kernel.nix new file mode 100644 index 0000000..5f8fd81 --- /dev/null +++ b/milkv/pioneer/linuxboot-kernel.nix @@ -0,0 +1,15 @@ +{ fetchFromGitHub, lib, linuxManualConfig, stdenv, ... }: + +linuxManualConfig rec { + inherit lib stdenv; + modDirVersion = "6.6.20"; + version = "${modDirVersion}-milkv-pioneer"; + src = fetchFromGitHub { + owner = "sophgo"; + repo = "linux-riscv"; + rev = "caa949e3690fe8a4656313b2b56f52666fa880db"; + hash = "sha256-qJpR3KMgvP4tfPfBfQ/MiEWg/uuuxHYuACK8taKKK3E="; + }; + configfile = "${src}/arch/riscv/configs/sophgo_mango_normal_defconfig"; + extraMeta.branch = "sg2042-dev-6.6"; +} diff --git a/milkv/pioneer/opensbi.nix b/milkv/pioneer/opensbi.nix new file mode 100644 index 0000000..5d77a7b --- /dev/null +++ b/milkv/pioneer/opensbi.nix @@ -0,0 +1,22 @@ +{ fetchFromGitHub, opensbi, ... }: + +opensbi.overrideAttrs (attrs: { + # Based on the vendor's sg2042-master branch. + version = "1.4-git-a6e158f7"; + src = fetchFromGitHub { + owner = "sophgo"; + repo = "opensbi"; + rev = "a6e158f71aab17155e2bf25a325ce4f0be51d9dd"; + hash = "sha256-5ggrEx1e53pB2+m0TBjDzDJXf2wjsQ2edu01FqqGt/Y="; + }; + + makeFlags = + # Based on the vendor options + # https://github.com/sophgo/bootloader-riscv/blob/01dc52ce10e7cf489c93e4f24b6bfe1bf6e55919/scripts/envsetup.sh#L299 + attrs.makeFlags ++ [ + "PLATFORM=generic" + "FW_PIC=y" + "BUILD_INFO=y" + "DEBUG=1" + ]; +}) diff --git a/milkv/pioneer/sd-image-installer.nix b/milkv/pioneer/sd-image-installer.nix new file mode 100644 index 0000000..1b900b0 --- /dev/null +++ b/milkv/pioneer/sd-image-installer.nix @@ -0,0 +1,12 @@ +{ modulesPath, ... }: + +{ + imports = [ + "${modulesPath}/profiles/installation-device.nix" + ./sd-image.nix + ]; + + # the installation media is also the installation target, + # so we don't want to provide the installation configuration.nix. + installer.cloneConfig = false; +} diff --git a/milkv/pioneer/sd-image.nix b/milkv/pioneer/sd-image.nix new file mode 100644 index 0000000..eaa378c --- /dev/null +++ b/milkv/pioneer/sd-image.nix @@ -0,0 +1,57 @@ +{ config, lib, modulesPath, pkgs, ... }: + +let + inherit (pkgs) callPackage; + + fip = callPackage ./fip.nix { }; + zsbl = callPackage ./zsbl.nix { }; + opensbi = callPackage ./opensbi.nix { }; + linuxboot-kernel = callPackage ./linuxboot-kernel.nix { }; + linuxboot-initrd = callPackage ./linuxboot-initrd.nix { }; + dtbs = config.hardware.deviceTree.package; + firmware = callPackage ./firmware.nix { + inherit fip zsbl opensbi linuxboot-kernel linuxboot-initrd dtbs; + }; +in +{ + imports = [ + "${modulesPath}/profiles/base.nix" + "${modulesPath}/installer/sd-card/sd-image.nix" + ./default.nix + ]; + + boot.loader = { + grub.enable = lib.mkDefault false; + generic-extlinux-compatible.enable = lib.mkDefault true; + }; + + hardware.enableRedistributableFirmware = true; + + # For some reason the serial getty is not started automatically + # even though console=ttyS0,115200 is passed to the kernel. + # https://docs.kernel.org/admin-guide/serial-console.html + # https://github.com/NixOS/nixpkgs/issues/84105 + systemd.services."serial-getty@ttyS0" = { + enable = true; + wantedBy = [ "getty.target" ]; + serviceConfig.Restart = "always"; + }; + + sdImage = { + imageName = "${config.sdImage.imageBaseName}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}-milkv-pioneer.img"; + + populateFirmwareCommands = '' + mkdir -p firmware/ + cp -a ${firmware}/* firmware/ + touch firmware/BOOT + ''; + + firmwarePartitionOffset = 1; + firmwareSize = 128; + + populateRootCommands = '' + mkdir -p ./files/boot + ${config.boot.loader.generic-extlinux-compatible.populateCmd} -c ${config.system.build.toplevel} -d ./files/boot + ''; + }; +} diff --git a/milkv/pioneer/zsbl-increase-timeout.patch b/milkv/pioneer/zsbl-increase-timeout.patch new file mode 100644 index 0000000..b1853f9 --- /dev/null +++ b/milkv/pioneer/zsbl-increase-timeout.patch @@ -0,0 +1,15 @@ +--- a/drivers/sd/sd.c ++++ b/drivers/sd/sd.c +@@ -560,11 +560,11 @@ static int bm_sd_read(int lba, uintptr_t buf, size_t size) + } else { + udelay(1); + timeout++; + } + +- if (timeout >= 10000) { ++ if (timeout >= 100000) { + printf("sdhci read data timeout\n"); + goto timeout; + } + } + diff --git a/milkv/pioneer/zsbl.nix b/milkv/pioneer/zsbl.nix new file mode 100644 index 0000000..a275d30 --- /dev/null +++ b/milkv/pioneer/zsbl.nix @@ -0,0 +1,51 @@ +{ buildPackages +, bison +, fetchFromGitHub +, flex +, lib +, stdenv + +, ... +}: +stdenv.mkDerivation rec { + pname = "zsbl-sg2042"; + version = "git-cc806273"; + src = fetchFromGitHub { + owner = "sophgo"; + repo = "zsbl"; + rev = "cc806273e0f679bef2f6b017c68adede1594ad31"; + hash = "sha256-zOlBM7mwz8FUM/BlzOxJmpI8LI/KcFOGXegvgiilbaM="; + }; + + patches = [ + # Depending on the sdcard, reading larger initrds (say >= 25MB) + # can hit the timeout. + ./zsbl-increase-timeout.patch + ]; + + nativeBuildInputs = [ + bison + flex + ]; + depsBuildBuild = [ + buildPackages.stdenv.cc + ]; + hardeningDisable = [ + "fortify" + "stackprotector" + ]; + + makeFlags = [ + "CROSS_COMPILE=${stdenv.cc.targetPrefix}" + ]; + configurePhase = "make sg2042_defconfig"; + installPhase = "install -D zsbl.bin $out"; + enableParallelBuilding = true; + dontStrip = true; + + meta = { + homepage = "https://github.com/sophgo/zsbl"; + description = "Sophgo RISC-V Zero Stage Boot Loader"; + license = lib.licenses.gpl2; + }; +}