├── .gitignore ├── tools.nix ├── .github └── workflows │ └── test.yml ├── tools ├── linux-checkout └── enter-kernel-dev ├── LICENSE ├── bindgen └── 0.65.1.nix ├── README.md ├── flake.lock └── flake.nix /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv 2 | /result 3 | -------------------------------------------------------------------------------- /tools.nix: -------------------------------------------------------------------------------- 1 | { 2 | stdenvNoCC, 3 | makeWrapper, 4 | flakeSelf, 5 | }: 6 | 7 | stdenvNoCC.mkDerivation { 8 | name = "kernel-dev-tools"; 9 | 10 | src = ./tools; 11 | 12 | nativeBuildInputs = [ 13 | makeWrapper 14 | ]; 15 | 16 | dontConfigure = true; 17 | dontBuild = true; 18 | 19 | installPhase = '' 20 | runHook preInstall 21 | 22 | mkdir -p $out/bin 23 | 24 | for script in *; do 25 | install -m0555 "$script" $out/bin/ 26 | done 27 | 28 | wrapProgram $out/bin/enter-kernel-dev \ 29 | --set FLAKE_DIR "${flakeSelf}" 30 | 31 | runHook preInstall 32 | ''; 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Test" 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | linux_build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: cachix/install-nix-action@v25 11 | - uses: cachix/cachix-action@v14 12 | with: 13 | name: blitz 14 | - name: Checkout Linux 15 | run: | 16 | git clone --depth 1 --branch ${{ matrix.version.tag }} https://github.com/gregkh/linux.git 17 | - name: Check Devshell 18 | run: | 19 | nix develop .#${{ matrix.version.attribute }} --command bash -c "cd linux && make rustavailable && make defconfig && make -j$(nproc) bzImage" 20 | git -C linux clean -fdx 21 | nix develop .#${{ matrix.version.attribute }}_gcc --command bash -c "cd linux && make defconfig && make -j$(nproc) bzImage" 22 | strategy: 23 | matrix: 24 | version: [ 25 | { tag: v6.6, attribute: linux_6_6 }, 26 | { tag: v6.12, attribute: linux_6_12 } 27 | ] 28 | -------------------------------------------------------------------------------- /tools/linux-checkout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | if [ $# -ne 1 ]; then 6 | echo "Usage: $0 output-directory" 1>&2 7 | echo 8 | echo "Check out a Linux development tree and populates it with typical remotes." 9 | exit 1 10 | fi 11 | 12 | OUTDIR=$1 13 | 14 | if [ -e "$OUTDIR" ]; then 15 | echo "$OUTDIR already exists. Bailing out." 1>&2 16 | exit 1 17 | fi 18 | 19 | mkdir -p "$OUTDIR" 20 | 21 | pushd "$OUTDIR" 22 | 23 | git init --initial-branch=torvalds 24 | 25 | git remote add torvalds https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 26 | git remote add stable https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git 27 | git remote add rust-for-linux https://github.com/Rust-for-Linux/linux.git 28 | git remote add kvm https://git.kernel.org/pub/scm/virt/kvm/kvm.git 29 | # Add new remotes here. 30 | 31 | echo "Fetching all remotes. This will take a while." 32 | git fetch --all 33 | 34 | git merge --ff-only torvalds/master 35 | 36 | # Reduces the size of the repo a lot. 37 | git gc --aggressive --prune=now 38 | 39 | popd 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Julian Stecklina 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 | -------------------------------------------------------------------------------- /bindgen/0.65.1.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | fetchCrate, 4 | rustPlatform, 5 | clang, 6 | rustfmt, 7 | }: 8 | let 9 | # bindgen hardcodes rustfmt outputs that use nightly features 10 | rustfmt-nightly = rustfmt.override { asNightly = true; }; 11 | in 12 | rustPlatform.buildRustPackage rec { 13 | pname = "rust-bindgen-unwrapped"; 14 | version = "0.65.1"; 15 | 16 | src = fetchCrate { 17 | pname = "bindgen-cli"; 18 | inherit version; 19 | sha256 = "sha256-9JJXQQSbCxTh3fIbVSrc6WAYGivwomkoB8ZIquUNr9o="; 20 | }; 21 | 22 | cargoHash = "sha256-Kz6Y+4F9Yu5oKYI9LgZKLh0AkQTwerPS4A758TZrkoc="; 23 | 24 | buildInputs = [ clang.cc.lib ]; 25 | 26 | preConfigure = '' 27 | export LIBCLANG_PATH="${clang.cc.lib}/lib" 28 | ''; 29 | 30 | doCheck = true; 31 | nativeCheckInputs = [ clang ]; 32 | 33 | RUSTFMT = "${rustfmt-nightly}/bin/rustfmt"; 34 | 35 | preCheck = '' 36 | # for the ci folder, notably 37 | patchShebangs . 38 | ''; 39 | 40 | passthru = { inherit clang; }; 41 | 42 | meta = with lib; { 43 | description = "Automatically generates Rust FFI bindings to C (and some C++) libraries"; 44 | longDescription = '' 45 | Bindgen takes a c or c++ header file and turns them into 46 | rust ffi declarations. 47 | ''; 48 | homepage = "https://github.com/rust-lang/rust-bindgen"; 49 | license = with licenses; [ bsd3 ]; 50 | maintainers = with maintainers; [ 51 | johntitor 52 | ralith 53 | ]; 54 | mainProgram = "bindgen"; 55 | platforms = platforms.unix; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Development Environment via Nix 2 | 3 | Welcome to a streamlined Linux kernel development setup using Nix 4 | flakes. This project simplifies the process of setting up a 5 | development environment for the Linux kernel, ensuring that you have 6 | all the necessary dependencies, including specific versions of Rust 7 | and bindgen, preinstalled in a devShell. 8 | 9 | ## Quick Start 10 | 11 | Getting started with your kernel development environment is incredibly 12 | simple. If you're aiming to develop for Linux version 6.12, all you 13 | need is a single command: 14 | 15 | ```shell 16 | linux$ nix develop github:blitz/kernel-dev#linux_6_12 17 | ``` 18 | 19 | This will drop you into a shell with all the dependencies required for 20 | working on the Linux 6.12 kernel preinstalled. Check the `devShells` attribute 21 | in `flake.nix` to find all available shells. 22 | 23 | ### Features 24 | 25 | - **Simplicity**: Forget about the hassle of managing multiple 26 | dependencies and their versions. One command is all it takes. 27 | - **Reproducibility**: With Nix flakes, your development environment 28 | is reproducible, ensuring that your setup is always consistent and 29 | predictable. 30 | - **Version-specific environments**: Tailored development environments 31 | for specific Linux kernel versions, starting with Linux 6.8. 32 | - **Rust and LLVM-enabled**: Ready for the future of kernel development! 33 | 34 | ### Requirements 35 | 36 | - Nix package manager with flake support enabled. 37 | 38 | ## Usage 39 | 40 | To start developing for a different version of the Linux kernel, use 41 | the following pattern, replacing linux_6_8 with the target version 42 | (note: versions are added based on community contributions and 43 | demand): 44 | 45 | ```shell 46 | $ nix develop github:blitz/kernel-dev#linux__ 47 | ``` 48 | 49 | This will drop you in a shell with `clang` and `rustc` matching the 50 | kernel version. If you need a `gcc`-based environment, use: 51 | 52 | ```shell 53 | $ nix develop github:blitz/kernel-dev#linux___gcc 54 | ``` 55 | 56 | If you don't find the exact version that you need, one that is close 57 | _might_ work as well. 58 | 59 | ## Contributing 60 | 61 | Contributions are welcome! Whether it's adding support for new kernel 62 | versions, improving the setup process, or documentation - feel free to 63 | fork the project and submit a pull request. 64 | 65 | ## License 66 | 67 | This project is open source and available under the MIT License. 68 | -------------------------------------------------------------------------------- /tools/enter-kernel-dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 6 | 7 | # When this script is used as is, SCRIPT_DIR is ok. If it's used as 8 | # part of kernelDevTools, we have to inject the path to the flake. 9 | if [ -z "$FLAKE_DIR" ]; then 10 | FLAKE_DIR="$SCRIPT_DIR" 11 | fi 12 | 13 | gcc_flag=false 14 | kernel_version="" 15 | 16 | # Parse command-line options with optional positional parameter handling 17 | TEMP=$(getopt -o 'h:' --longoptions 'gcc,help' -n 'script_name' -- "$@") 18 | 19 | eval set -- "$TEMP" 20 | 21 | # Extract options 22 | while true; do 23 | case "$1" in 24 | --gcc) 25 | gcc_flag=true 26 | shift 27 | ;; 28 | -h | --help) 29 | echo "Usage: $0 [--gcc] [KERNEL_VERSION]" 30 | exit 0 31 | ;; 32 | --) 33 | shift 34 | # Check if there's an argument after -- 35 | if [[ -n "$1" ]]; then 36 | kernel_version="$1" 37 | fi 38 | break 39 | ;; 40 | *) 41 | echo "Error: Invalid option" 42 | echo "Usage: $0 [--gcc] [KERNEL_VERSION]" 43 | exit 1 44 | ;; 45 | esac 46 | done 47 | 48 | if [ -z "$kernel_version" ]; then 49 | 50 | latest_release=$(git tag --contains=HEAD | grep -oE 'v[0-9]+\.[0-9]+' | sort | head -n1) 51 | 52 | if [ -z "$latest_release" ]; then 53 | tput bold 54 | echo -n "Failed to automatically recognize kernel version." 55 | tput sgr0 56 | echo " This could have different reasons:" 57 | echo 58 | echo "0. This is a development version of Linux and not part of any release yet. Try manually selecting a version:" 59 | echo " \$ enter-kernel-dev v6.10" 60 | echo 61 | echo "1. This is not a Git checkout of the Linux kernel sources. To checkout Linux, try:" 62 | echo " \$ linux-checkout linux-src" 63 | echo 64 | echo "2. The repository has no remote that contains the version tags. Try:" 65 | echo " \$ git remote add stable git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git" 66 | echo " \$ git fetch stable" 67 | exit 1 68 | fi 69 | 70 | kernel_version="$latest_release" 71 | fi 72 | 73 | if ! echo $kernel_version | grep -qE 'v[0-9]+\.[0-9]+'; then 74 | tput bold 75 | echo "Invalid kernel version format: $kernel_version" 76 | tput sgr0 77 | echo 78 | echo "Kernel versions should look like: v6.10" 79 | exit 1 80 | fi 81 | 82 | ATTRIBUTE="linux_$(echo "$kernel_version" | tr -d 'v' | tr '.' '_')" 83 | 84 | if $gcc_flag; then 85 | ATTRIBUTE=${ATTRIBUTE}_gcc 86 | fi 87 | 88 | echo -n "Entering environment " 89 | tput bold 90 | echo -n "$ATTRIBUTE" 91 | tput sgr0 # reset 92 | echo ". This might take a moment. Exit via ^D." 93 | 94 | # TODO It would be nice to gracefully fail if we don't have an 95 | # environment for a particular kernel version yet. 96 | 97 | exec nix develop "$FLAKE_DIR"\#"$ATTRIBUTE" --command "$SHELL" 98 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "fenix": { 4 | "inputs": { 5 | "nixpkgs": [ 6 | "nixpkgs" 7 | ], 8 | "rust-analyzer-src": "rust-analyzer-src" 9 | }, 10 | "locked": { 11 | "lastModified": 1740378829, 12 | "narHash": "sha256-cwmm7F73aQFJY6YN1roNibNKwxT6FlfXkG3MEbpSp7Q=", 13 | "owner": "nix-community", 14 | "repo": "fenix", 15 | "rev": "92823f1b0c919d7e2d806956aaf98e90f3761ab7", 16 | "type": "github" 17 | }, 18 | "original": { 19 | "owner": "nix-community", 20 | "repo": "fenix", 21 | "type": "github" 22 | } 23 | }, 24 | "flake-utils": { 25 | "inputs": { 26 | "systems": [ 27 | "systems" 28 | ] 29 | }, 30 | "locked": { 31 | "lastModified": 1731533236, 32 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 33 | "owner": "numtide", 34 | "repo": "flake-utils", 35 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 36 | "type": "github" 37 | }, 38 | "original": { 39 | "owner": "numtide", 40 | "repo": "flake-utils", 41 | "type": "github" 42 | } 43 | }, 44 | "nixpkgs": { 45 | "locked": { 46 | "lastModified": 1740126099, 47 | "narHash": "sha256-ozoOtE2hGsqh4XkTJFsrTkNxkRgShxpQxDynaPZUGxk=", 48 | "owner": "nixos", 49 | "repo": "nixpkgs", 50 | "rev": "32fb99ba93fea2798be0e997ea331dd78167f814", 51 | "type": "github" 52 | }, 53 | "original": { 54 | "owner": "nixos", 55 | "ref": "nixos-unstable", 56 | "repo": "nixpkgs", 57 | "type": "github" 58 | } 59 | }, 60 | "root": { 61 | "inputs": { 62 | "fenix": "fenix", 63 | "flake-utils": "flake-utils", 64 | "nixpkgs": "nixpkgs", 65 | "rust-overlay": "rust-overlay", 66 | "systems": "systems" 67 | } 68 | }, 69 | "rust-analyzer-src": { 70 | "flake": false, 71 | "locked": { 72 | "lastModified": 1740329432, 73 | "narHash": "sha256-eKQ7aBkNvF5AhUpyJ1cW450jxomZ4gTIaYir5qsNl7Y=", 74 | "owner": "rust-lang", 75 | "repo": "rust-analyzer", 76 | "rev": "6d68c475c7aaf7534251182662456a4bf4216dfe", 77 | "type": "github" 78 | }, 79 | "original": { 80 | "owner": "rust-lang", 81 | "ref": "nightly", 82 | "repo": "rust-analyzer", 83 | "type": "github" 84 | } 85 | }, 86 | "rust-overlay": { 87 | "inputs": { 88 | "nixpkgs": [ 89 | "nixpkgs" 90 | ] 91 | }, 92 | "locked": { 93 | "lastModified": 1740364262, 94 | "narHash": "sha256-X5EtT29uEtXN2E4bDiDU2HGBdmFHjHf1KbP6iKP0cmg=", 95 | "owner": "oxalica", 96 | "repo": "rust-overlay", 97 | "rev": "7c5892ad87b90d72668964975eebd4e174ff6204", 98 | "type": "github" 99 | }, 100 | "original": { 101 | "owner": "oxalica", 102 | "repo": "rust-overlay", 103 | "type": "github" 104 | } 105 | }, 106 | "systems": { 107 | "locked": { 108 | "lastModified": 1689347949, 109 | "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", 110 | "owner": "nix-systems", 111 | "repo": "default-linux", 112 | "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", 113 | "type": "github" 114 | }, 115 | "original": { 116 | "owner": "nix-systems", 117 | "repo": "default-linux", 118 | "type": "github" 119 | } 120 | } 121 | }, 122 | "root": "root", 123 | "version": 7 124 | } 125 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Kernel development environments"; 3 | 4 | inputs = { 5 | systems.url = "github:nix-systems/default-linux"; 6 | 7 | nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; 8 | 9 | flake-utils = { 10 | url = "github:numtide/flake-utils"; 11 | inputs.systems.follows = "systems"; 12 | }; 13 | 14 | rust-overlay = { 15 | url = "github:oxalica/rust-overlay"; 16 | inputs.nixpkgs.follows = "nixpkgs"; 17 | }; 18 | 19 | fenix = { 20 | url = "github:nix-community/fenix"; 21 | inputs.nixpkgs.follows = "nixpkgs"; 22 | }; 23 | }; 24 | 25 | outputs = 26 | { 27 | self, 28 | nixpkgs, 29 | rust-overlay, 30 | fenix, 31 | flake-utils, 32 | ... 33 | }: 34 | flake-utils.lib.eachDefaultSystem ( 35 | system: 36 | let 37 | pkgs = nixpkgs.legacyPackages."${system}"; 38 | 39 | # A set of scripts to simplify kernel development. 40 | kernelDevTools = pkgs.callPackage ./tools.nix { 41 | flakeSelf = self; 42 | }; 43 | 44 | linuxCommonDependencies = 45 | [ 46 | kernelDevTools 47 | ] 48 | ++ (with pkgs; [ 49 | bc 50 | bison 51 | cpio 52 | elfutils 53 | flex 54 | gmp 55 | gnumake 56 | kmod 57 | libmpc 58 | mpfr 59 | nettools 60 | openssl 61 | pahole 62 | perl 63 | python3Minimal 64 | rsync 65 | ubootTools 66 | zlib 67 | zstd 68 | 69 | # For make menuconfig 70 | ncurses 71 | 72 | # For make gtags 73 | global 74 | 75 | # For git send-email 🫠 76 | gitFull 77 | ]); 78 | 79 | rust-analyzer = fenix.packages."${system}".rust-analyzer; 80 | 81 | linuxRustDependencies = 82 | { clang, rustVersion }: 83 | let 84 | rustc = rust-overlay.packages."${system}"."${rustVersion}".override { 85 | extensions = [ 86 | "rust-src" 87 | "rustfmt" 88 | "clippy" 89 | ]; 90 | }; 91 | 92 | rustPlatform = pkgs.makeRustPlatform { 93 | cargo = rustc; 94 | rustc = rustc; 95 | }; 96 | 97 | bindgenUnwrapped = pkgs.callPackage ./bindgen/0.65.1.nix { 98 | inherit rustPlatform clang; 99 | }; 100 | 101 | bindgen = pkgs.rust-bindgen.override { 102 | rust-bindgen-unwrapped = bindgenUnwrapped; 103 | }; 104 | in 105 | [ 106 | bindgen 107 | rust-analyzer 108 | rustc 109 | ]; 110 | 111 | mkGccShell = 112 | { gccVersion }: 113 | pkgs.mkShell { 114 | packages = linuxCommonDependencies ++ [ pkgs."gcc${gccVersion}" ]; 115 | 116 | # Disable all automatically applied hardening. The Linux 117 | # kernel will take care of itself. 118 | NIX_HARDENING_ENABLE = ""; 119 | }; 120 | 121 | mkClangShell = 122 | { clangVersion, rustcVersion }: 123 | let 124 | llvmPackages = pkgs."llvmPackages_${clangVersion}"; 125 | in 126 | pkgs.mkShell { 127 | packages = 128 | (with llvmPackages; [ 129 | bintools 130 | clang 131 | llvm 132 | ]) 133 | ++ (linuxRustDependencies { 134 | inherit (llvmPackages) clang; 135 | rustVersion = "rust_${rustcVersion}"; 136 | }) 137 | ++ linuxCommonDependencies; 138 | 139 | # To force LLVM build mode. This should create less problems 140 | # with Rust interop. 141 | LLVM = "1"; 142 | 143 | # Disable all automatically applied hardening. The Linux 144 | # kernel will take care of itself. 145 | NIX_HARDENING_ENABLE = ""; 146 | }; 147 | in 148 | { 149 | packages = { 150 | inherit kernelDevTools; 151 | default = kernelDevTools; 152 | }; 153 | 154 | devShells = { 155 | default = self.devShells."${system}".linux_6_12; 156 | 157 | linux_6_6 = mkClangShell { 158 | clangVersion = "19"; 159 | rustcVersion = "1_78_0"; 160 | }; 161 | linux_6_6_gcc = mkGccShell { gccVersion = "14"; }; 162 | 163 | linux_6_11 = mkClangShell { 164 | clangVersion = "19"; 165 | rustcVersion = "1_78_0"; 166 | }; 167 | linux_6_11_gcc = mkGccShell { gccVersion = "14"; }; 168 | 169 | linux_6_12 = mkClangShell { 170 | clangVersion = "19"; 171 | rustcVersion = "1_82_0"; 172 | }; 173 | linux_6_12_gcc = mkGccShell { gccVersion = "14"; }; 174 | }; 175 | 176 | formatter = pkgs.nixfmt-rfc-style; 177 | } 178 | ); 179 | } 180 | --------------------------------------------------------------------------------