diff --git a/.Dockerignore b/.Dockerignore deleted file mode 100644 index 76dff14..0000000 --- a/.Dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!linux/* -linux/.git diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index bfa55af..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,415 +0,0 @@ -name: Compile and release - -on: - # Releases are cut on merges into main and on dev_* tags (auto-versioned - # below, like rehosting/penguin). NOTE: we deliberately do NOT trigger on - # 'v*' tags — the release step now *creates* v* tags via version-increment, - # so a 'v*' push trigger would make every release re-trigger itself. - push: - branches: - - main - tags: - - 'dev_*' - - pull_request: - branches: - - main - - workflow_dispatch: - -# Serialize per-ref so two main merges (or a merge + dev_* tag) can't race the -# auto-version step and compute/claim the same vX.Y.Z tag. PR runs cancel their -# own superseded runs; release (push) runs are never cancelled. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - prebuild: - runs-on: rehosting-arc - outputs: - targets: ${{ steps.find_targets.outputs.targets }} - versions: ${{ steps.find_targets.outputs.versions }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} - - - name: Log git revisions of all linux projects - run: | - echo "Main repo revision:" && git rev-parse HEAD - echo - echo "Submodule revisions:" && git submodule status - echo - echo "Full submodule SHAs:" && git submodule foreach 'echo $name: $(git rev-parse HEAD)' - - # NOTE: kernel-source preparation used to live here and wrote to the shared - # hostPath on prebuild's node, which forced every build job onto that same - # node. It now happens per-node inside each build job (see "Ensure kernel - # sources on this node" below), so prebuild only needs to discover the - # build matrix -- nothing node-specific. - - - name: Find valid targets and versions sets - id: find_targets - run: | - TARGETS_SET=() - VERSIONS_SET=() - for version_dir in configs/*/; do - version=$(basename "$version_dir") - VERSIONS_SET+=("$version") - for config_file in "$version_dir"*; do - if [[ -f "$config_file" && ! "$config_file" =~ \.inc$ && ! "$config_file" =~ \.unused$ ]]; then - target=$(basename "$config_file") - TARGETS_SET+=("$target") - fi - done - done - UNIQUE_TARGETS=$(printf "%s\n" "${TARGETS_SET[@]}" | sort -u | awk '{printf "\"%s\",",$0}' | sed 's/,$//') - UNIQUE_VERSIONS=$(printf "%s\n" "${VERSIONS_SET[@]}" | sort -u | awk '{printf "\"%s\",",$0}' | sed 's/,$//') - TARGETS_OUTPUT="[${UNIQUE_TARGETS}]" - VERSIONS_OUTPUT="[${UNIQUE_VERSIONS}]" - echo "targets=$TARGETS_OUTPUT" >> $GITHUB_OUTPUT - echo "versions=$VERSIONS_OUTPUT" >> $GITHUB_OUTPUT - echo "Found valid targets: $TARGETS_OUTPUT" - echo "Found valid versions: $VERSIONS_OUTPUT" - - # Build the kernel_builder image exactly once per run, push it to Harbor - # under an immutable per-commit tag, and let every matrix build job pull it. - # - # Why a dedicated job (instead of building inside each matrix job): - # * The image is now target-agnostic (TARGET=latest bundles every - # cross-toolchain), so all 13 build jobs would otherwise rebuild the SAME - # multi-GB image. setup-buildx-action's docker-container driver keeps its - # own cache store, separate from the shared per-node Docker daemon, so the - # daemon's warm-image reuse does NOT cover a buildx `FROM` base -- every - # job re-pulled the full all-arch base. Building once here fixes that. - # * A single writer to the `:all_cache` registry cache means the cache - # actually accumulates run-to-run instead of 13 jobs racing to overwrite - # one ref. - # * An immutable `:` tag (vs a mutable `:latest`) is safe on the shared - # daemon's single image store -- concurrent jobs/PRs can't clobber each - # other's tag (same lesson as rehosting/penguin #893). - build-image: - runs-on: rehosting-arc - if: github.event.pull_request.draft == false - steps: - - uses: actions/checkout@v4 - - - name: Trust Harbor's self-signed certificate - run: | - echo "Fetching certificate from ${{ secrets.REHOSTING_ARC_REGISTRY }}" - openssl s_client -showcerts -connect ${{ secrets.REHOSTING_ARC_REGISTRY }}:443 < /dev/null 2>/dev/null | openssl x509 -outform PEM | sudo tee /usr/local/share/ca-certificates/harbor.crt > /dev/null - sudo update-ca-certificates - - - name: Log in to Rehosting Arc Registry - uses: docker/login-action@v3 - with: - registry: ${{secrets.REHOSTING_ARC_REGISTRY}} - username: ${{ secrets.REHOSTING_ARC_REGISTRY_USER }} - password: ${{ secrets.REHOSTING_ARC_REGISTRY_PASSWORD }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - # See the note in the (removed) per-job build step / penguin c35bedc5: - # don't pin moby/buildkit:master -- it regressed on the kernel-5.4 - # runners (can't mask /proc/acpi). Default pinned-stable buildkit + the - # insecure Harbor registry config. - driver-opts: | - network=host - buildkitd-config-inline: | - [registry."${{ secrets.REHOSTING_ARC_REGISTRY }}"] - insecure = true - http = true - - - name: Build and push kernel_builder image - uses: docker/build-push-action@v6 - with: - context: . - push: true - # Immutable per-commit tag -- matrix jobs pull this exact ref. - tags: | - ${{ secrets.REHOSTING_ARC_REGISTRY }}/rehosting/linux_builder:${{ github.sha }} - # TARGET=latest builds FROM the all-arch embedded-toolchains:latest so - # the single built image carries every cross-toolchain and one image - # serves every matrix target. - build-args: | - REGISTRY=${{ secrets.REHOSTING_ARC_REGISTRY }}/proxy - TARGET=latest - # Single shared build cache, written by this one job (no matrix race). - cache-from: | - type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:all_cache,mode=max - cache-to: | - type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:all_cache,mode=max - - build: - needs: [prebuild, build-image] - runs-on: rehosting-arc - if: github.event.pull_request.draft == false - - strategy: - matrix: - target_version: ${{ fromJSON(needs.prebuild.outputs.targets) }} - - env: - # The image built once by build-image; pulled (warm on the shared daemon - # after the first job lands on a node) instead of rebuilt per target. - KERNEL_BUILDER_IMAGE: ${{ secrets.REHOSTING_ARC_REGISTRY }}/rehosting/linux_builder:${{ github.sha }} - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref }} - - - name: Extract target and version - id: extract - run: | - TARGET="${{ matrix.target_version }}" - echo "target=$TARGET" >> $GITHUB_OUTPUT - echo "Building target: $TARGET" - - - name: Ensure kernel sources on this node - id: sources - run: | - set -eux - SHARED="/home/runner/_shared" - BASE_REPO_DIR="$SHARED/linux" - BASE_REPO_URL="https://github.com/rehosting/linux" - SRC_PARENT="$SHARED/linux_sources" - mkdir -p "$SRC_PARENT" - - # Cache key = the pinned linux/ submodule SHAs. The kernel source - # only changes when a submodule is bumped, so a node reuses its tree - # across runs and only re-populates on a real SHA change. - KEY=$(git submodule status | awk '{gsub(/^[-+U ]+/,"",$1); print $1}' | sort | sha1sum | cut -c1-12) - SRC_ROOT="$SRC_PARENT/$KEY" - echo "kernel_src=$SRC_ROOT/linux" >> "$GITHUB_OUTPUT" - - # Per-node arbitration: whichever build job lands on a node first - # populates that node's copy; the others block on the lock, then see - # .ready and skip. flock on the shared fs is the right primitive -- - # GH 'concurrency' is cross-node and can't serialize same-node jobs. - # This also avoids the cp/rsync races the single shared dir hit. - exec 9>"$SRC_PARENT/.populate.lock" - flock 9 - - if [ ! -e "$SRC_ROOT/.ready" ]; then - echo "Populating kernel sources for key $KEY on $(hostname)" - # Node-local bare clone so submodule update pulls over fast local - # file:// instead of hitting GitHub once per submodule per job. - if [ ! -d "$BASE_REPO_DIR" ]; then - git clone --bare "$BASE_REPO_URL" "$BASE_REPO_DIR" - git -C "$BASE_REPO_DIR" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" - fi - git -C "$BASE_REPO_DIR" fetch origin --prune --tags --force - - rm -rf "$SRC_ROOT.tmp" - mkdir -p "$SRC_ROOT.tmp" - # Copy the (submodule-less) superproject checkout, then init the - # kernel submodules into it from the local bare clone. - cp -a "$GITHUB_WORKSPACE/." "$SRC_ROOT.tmp/" - ( cd "$SRC_ROOT.tmp" - sed -i "s|url = https://github.com/rehosting/linux.git|url = file://$BASE_REPO_DIR|g" .gitmodules - git submodule sync - GIT_ALLOW_PROTOCOL=file:https git submodule update --init --depth 1 --jobs 2 ) - # Publish atomically so a partial tree is never seen as ready. - rm -rf "$SRC_ROOT" - mv "$SRC_ROOT.tmp" "$SRC_ROOT" - touch "$SRC_ROOT/.ready" - else - echo "Reusing cached kernel sources for key $KEY on $(hostname)" - fi - # Record last-use so the GC below doesn't reap an actively-reused tree. - touch "$SRC_ROOT" - - # Best-effort GC: drop keyed trees (and stale .tmp dirs) untouched for - # 14 days so the node-local cache can't grow unbounded across bumps. - find "$SRC_PARENT" -mindepth 1 -maxdepth 1 -type d ! -path "$SRC_ROOT" -mtime +14 \ - -exec rm -rf {} + 2>/dev/null || true - - flock -u 9 - - - name: Trust Harbor's self-signed certificate - run: | - echo "Fetching certificate from ${{ secrets.REHOSTING_ARC_REGISTRY }}" - openssl s_client -showcerts -connect ${{ secrets.REHOSTING_ARC_REGISTRY }}:443 < /dev/null 2>/dev/null | openssl x509 -outform PEM | sudo tee /usr/local/share/ca-certificates/harbor.crt > /dev/null - sudo update-ca-certificates - - - name: Log in to Rehosting Arc Registry - uses: docker/login-action@v3 - with: - registry: ${{secrets.REHOSTING_ARC_REGISTRY}} - username: ${{ secrets.REHOSTING_ARC_REGISTRY_USER }} - password: ${{ secrets.REHOSTING_ARC_REGISTRY_PASSWORD }} - - - name: Pull kernel_builder image - run: | - set -eux - # Built once by the build-image job. On the shared per-node Docker - # daemon the first matrix job to land on a node pulls the layers; every - # later job on that node finds them already present (warm reuse). No - # per-job buildx build anymore. - docker pull "$KERNEL_BUILDER_IMAGE" - - - name: Build Kernel for ${{ matrix.target_version }} - run: | - set -eux - TARGET="${{ matrix.target_version }}" - VERSIONS_JSON='${{ needs.prebuild.outputs.versions }}' - # Per-node kernel sources prepared by the "Ensure kernel sources on - # this node" step above (node-agnostic: no dependency on prebuild's - # node). - SOURCES_DIR="${{ steps.sources.outputs.kernel_src }}" - - if [ -z "$VERSIONS_JSON" ] || [ "$VERSIONS_JSON" = "[]" ]; then - VERSIONS="" - else - VERSIONS=$(echo "$VERSIONS_JSON" | jq -r '.[]' | xargs) - fi - - # Node-shared, persistent compiler cache. Lives under the same - # /home/runner/_shared mount the kernel sources use (mirrored into the - # shared Docker daemon by kube#15), so mounting it into the build - # container needs no extra plumbing. One dir shared by every - # target/version on the node -- ccache is content-keyed and dedups, so - # a warm cache turns the ~15-20 min from-scratch compile into a mostly- - # cache-hit rebuild. _in_container_build.sh enables ccache iff CCACHE_DIR - # is set (no-op otherwise). - CCACHE_HOST_DIR="/home/runner/_shared/linux_builder_ccache" - mkdir -p "$CCACHE_HOST_DIR" - - # Use the prebuilt image (--image) and mount the stable source - # directory instead of the run-specific one, plus the shared ccache. - ./build.sh --image "$KERNEL_BUILDER_IMAGE" --targets "$TARGET" ${VERSIONS:+--versions "$VERSIONS"} \ - --extra-docker-opts "-v $SOURCES_DIR:/app/linux -v $CCACHE_HOST_DIR:/ccache -e CCACHE_DIR=/ccache -e CCACHE_MAXSIZE=15G" - - # Stage per-target outputs in the workspace; they are handed to the - # aggregate job via workflow artifacts (below) instead of a shared - # hostPath, so build and aggregate need not run on the same node. - mkdir -p build-output - mv kernels-latest.tar.gz build-output/kernels-latest-${TARGET}.tar.gz - mv kernel-devel-all.tar.gz build-output/kernel-devel-all-${TARGET}.tar.gz - - - name: Upload per-target kernel artifacts - uses: actions/upload-artifact@v4 - with: - name: kernels-${{ matrix.target_version }} - path: build-output/ - retention-days: 1 - - aggregate: - # Runs on releases (push to main / dev_* tag) AND on manual dispatch so the - # full download+combine round-trip can be exercised without cutting a - # release (the publish step below is gated to push events only). Never runs - # for pull_request. - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - needs: build - runs-on: rehosting-arc - env: - MATRIX_VERSIONS: ${{ toJSON(needs.build.strategy.matrix.version) }} - permissions: - actions: write - contents: write - steps: - - name: Trust Harbor's self-signed certificate - run: | - echo "Fetching certificate from ${{ secrets.REHOSTING_ARC_REGISTRY }}" - openssl s_client -showcerts -connect ${{ secrets.REHOSTING_ARC_REGISTRY }}:443 < /dev/null 2>/dev/null | openssl x509 -outform PEM | sudo tee /usr/local/share/ca-certificates/harbor.crt > /dev/null - sudo update-ca-certificates - - name: Log in to Rehosting Arc Registry - uses: docker/login-action@v3 - with: - registry: ${{secrets.REHOSTING_ARC_REGISTRY}} - username: ${{ secrets.REHOSTING_ARC_REGISTRY_USER }} - password: ${{ secrets.REHOSTING_ARC_REGISTRY_PASSWORD }} - - - name: Download all per-target kernel artifacts - uses: actions/download-artifact@v4 - with: - pattern: kernels-* - path: build-output - merge-multiple: true - - - name: Combine all kernels into a single archive - run: | - set -eux - # Artifacts downloaded by the step above land here (workspace-local, - # node-agnostic) instead of the old /home/runner/_shared/runs hostPath. - BUILD_OUTPUT="$GITHUB_WORKSPACE/build-output" - - echo "[DEBUG] Listing available per-target kernel archives:" - find "$BUILD_OUTPUT" -maxdepth 1 -name "kernels-latest-*.tar.gz" -print || true - - rm -rf combined-kernels && mkdir combined-kernels - - for archive in "$BUILD_OUTPUT"/kernels-latest-*.tar.gz; do - [ -e "$archive" ] || continue - echo "[DEBUG] Extracting $archive into combined-kernels" - tar -xzf "$archive" -C combined-kernels - done - - echo "[DEBUG] Contents of combined-kernels after extraction:" - find combined-kernels || true - - # Merge osi.config for every detected version directory - if [ -d combined-kernels/kernels ]; then - for vdir in combined-kernels/kernels/*; do - [ -d "$vdir" ] || continue - version=$(basename "$vdir") - echo "[DEBUG] Merging osi.config for version $version" - { - for archive in "$BUILD_OUTPUT"/kernels-latest-*.tar.gz; do - [ -e "$archive" ] || continue - tar -O -xf "$archive" "kernels/$version/osi.config" 2>/dev/null || true - done - } > "combined-kernels/kernels/$version/osi.config" - done - fi - - tar -czvf kernels-latest.tar.gz -C combined-kernels . - - - name: Aggregate all kernel-devel artifacts - run: | - set -eux - # Artifacts downloaded by the step above land here (workspace-local, - # node-agnostic) instead of the old /home/runner/_shared/runs hostPath. - BUILD_OUTPUT="$GITHUB_WORKSPACE/build-output" - - mkdir -p kernel-devel-all - for archive in "$BUILD_OUTPUT"/kernel-devel-all-*.tar.gz; do - [ -e "$archive" ] || continue - echo "[DEBUG] Extracting $archive into kernel-devel-all/" - tar -xzf "$archive" -C kernel-devel-all - done - tar -czvf kernel-devel-all.tar.gz -C kernel-devel-all . - - # Auto-version like rehosting/penguin: query the GitHub API for the latest - # release and increment. On main this yields a clean vX.Y.Z; on a non-main - # ref (a dev_* tag) version-increment appends a -pre suffix. - - name: Get next version - id: version - uses: reecetech/version-increment@2023.10.1 - with: - use_api: true - - - name: Create and publish release - # Only publish on real release events (main merge / dev_* tag). A manual - # workflow_dispatch still runs everything above to validate the pipeline - # but does not create a release. - if: github.event_name == 'push' - uses: softprops/action-gh-release@v1 - with: - files: | - kernels-latest.tar.gz - kernel-devel-all.tar.gz - token: ${{ secrets.GITHUB_TOKEN }} - tag_name: ${{ steps.version.outputs.v-version }} - name: Release ${{ steps.version.outputs.v-version }} - generate_release_notes: true - # dev_* tags publish as prereleases; main merges as full releases. - prerelease: ${{ startsWith(github.ref, 'refs/tags/dev_') }} - # (Removed the per-run /home/runner/_shared/runs cleanup: outputs now flow - # through workflow artifacts, which expire on their own retention, and the - # workspace build-output dir is ephemeral.) diff --git a/.github/workflows/clear_cache.yml b/.github/workflows/clear_cache.yml deleted file mode 100644 index 0c16273..0000000 --- a/.github/workflows/clear_cache.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Clear kernel cache - -on: - workflow_dispatch: - -jobs: - clear-cache: - runs-on: rehosting-arc - steps: - - name: Trust Harbor's self-signed certificate - run: | - echo "Fetching certificate from ${{ secrets.REHOSTING_ARC_REGISTRY }}" - openssl s_client -showcerts -connect ${{ secrets.REHOSTING_ARC_REGISTRY }}:443 < /dev/null 2>/dev/null | openssl x509 -outform PEM | sudo tee /usr/local/share/ca-certificates/harbor.crt > /dev/null - sudo update-ca-certificates - - uses: oras-project/setup-oras@v1.2.3 - - name: Log in to Rehosting Arc Registry - uses: docker/login-action@v3 - with: - registry: ${{ secrets.REHOSTING_ARC_REGISTRY }} - username: ${{ secrets.REHOSTING_ARC_REGISTRY_USER }} - password: ${{ secrets.REHOSTING_ARC_REGISTRY_PASSWORD }} - - name: Delete all kernel cache images - run: | - set -e - repo="${{ secrets.REHOSTING_ARC_REGISTRY }}/rehosting/linux_builder_cache" - tags=$(oras repo tags $repo) - for tag in $tags; do - echo "Deleting $repo:$tag" - oras repo rm $repo:$tag || echo "Failed to delete $repo:$tag" - done diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml new file mode 100644 index 0000000..2b39bb4 --- /dev/null +++ b/.github/workflows/nix.yml @@ -0,0 +1,304 @@ +# The patch-series gate, plus a representative build per version. +# +# Without this gate the series model degrades silently: someone edits a tree, +# forgets to export, and the committed series stops describing reality. "The +# series applies cleanly to the pinned upstream tarball" is the invariant that +# makes patches-as-source-of-truth safe. +# +# Nix install + Cachix wiring comes from rehosting/ci/actions/nix-setup, the +# org's shared action. Per its skip-push default, PR runs PULL from +# rehosting-tools but do not push; pushes to main populate it. +name: nix (patch series + kernels) + +on: + pull_request: + push: + branches: [main, nix-patchset] + # Cut a nix-built PRERELEASE from a tag, without touching the version line. + # + # The prefix is `nixdev_*` rather than `dev_*` for a reason that has now + # expired: `dev_*` triggered the Docker workflow (build.yml), and a shared + # prefix would have had both pipelines cut a release for the same tag and + # race `version-increment` for the same vX.Y.Z. build.yml is gone, so the + # collision is impossible -- but the prefix stays, because nixdev_0.1.0 and + # nixdev_0.1.1 are already published and pinned by downstream lockfiles. + tags: ['nixdev_*'] + workflow_dispatch: + inputs: + full_matrix: + description: "Build every cell (kernel + perf + driver), not just the gate" + type: boolean + default: false + +concurrency: + group: nix-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +# The Arc pods run as a uid with no passwd entry, so $USER/$LOGNAME are unset +# and HOME-relative caches are not writable. `cachix use` fails outright with +# "$USER must be set. If running in a container, try setting USER=root." These +# four are a precondition for running Nix on this runner pool, not decoration -- +# rehosting/qemu's workflow carries the same block for the same reason. +env: + USER: runner + LOGNAME: runner + TMPDIR: /home/runner/_work/_temp + XDG_CACHE_HOME: /home/runner/_work/.cache + +jobs: + series: + name: series applies (${{ matrix.version }}) + runs-on: rehosting-arc + strategy: + fail-fast: false + matrix: + version: ["4.10", "6.13"] + steps: + - uses: actions/checkout@v4 + + - name: Prepare runner directories + run: mkdir -p "$TMPDIR" "$XDG_CACHE_HOME" + + - name: Set up Nix + uses: rehosting/ci/actions/nix-setup@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + cache-backend: cachix + cachix-name: rehosting-tools + cachix-auth-token: ${{ secrets.CACHIX_REHOSTING }} + # No VM needed; avoids install-nix-action's KVM/udevadm step, which + # fails on Arc pods that expose /dev/kvm without a working udevd. + enable-kvm: false + extra-nix-config: | + max-jobs = 8 + cores = 8 + + - name: Fetch the pinned upstream tarball + id: tarball + run: | + set -euo pipefail + TAG=$(python3 -c "import json;print(json.load(open('patches/base.json'))['${{ matrix.version }}']['tag'])") + MAJOR="${TAG%%.*}" + URL="https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${TAG}.tar.xz" + echo "path=$(nix-prefetch-url --print-path "$URL" | tail -1)" >> "$GITHUB_OUTPUT" + + - name: Series applies cleanly to pristine upstream + run: ./scripts/verify-series.sh "${{ matrix.version }}" "${{ steps.tarball.outputs.path }}" + + # The fast gate: one cell per version, and for each of them the three + # artifacts that have historically failed independently of the kernel -- + # perf (silently skipped for 10 of 13 targets in the shell build), and the + # module (which is what proves the `dev` build tree is actually usable). + build: + name: build ${{ matrix.cell }} + runs-on: rehosting-arc + needs: series + strategy: + fail-fast: false + matrix: + cell: ["4.10-armel", "6.13-armel"] + steps: + - uses: actions/checkout@v4 + - name: Prepare runner directories + run: mkdir -p "$TMPDIR" "$XDG_CACHE_HOME" + - name: Set up Nix + uses: rehosting/ci/actions/nix-setup@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + cache-backend: cachix + cachix-name: rehosting-tools + cachix-auth-token: ${{ secrets.CACHIX_REHOSTING }} + # No VM needed; avoids install-nix-action's KVM/udevadm step, which + # fails on Arc pods that expose /dev/kvm without a working udevd. + enable-kvm: false + extra-nix-config: | + max-jobs = 8 + cores = 8 + + - name: Build kernel, perf and module + run: | + set -euo pipefail + for out in kernel perf driver; do + nix build --print-out-paths \ + ".#packages.x86_64-linux.\"$out-${{ matrix.cell }}\"" + done + + # Does the kernel actually RUN. Every other gate here passed the + # 4.10/x86_64 image that printed nothing at all -- see nix/boot.nix. + - name: Kernel boots + run: | + nix build --print-build-logs \ + ".#packages.x86_64-linux.\"boot-${{ matrix.cell }}\"" + + + # The full 19-cell matrix: on demand, on a `nixdev_*` tag, and on a merge to + # main -- in the last two cases it is also what produces the release. Not on + # every PR: it is the expensive one, and the gate above catches the failures + # that are not arch-specific. + # + # Running it on the tag has a second effect that matters for downstream + # testing: this is a push event, so nix-setup's skip-push default does NOT + # apply and every path built here lands in rehosting-tools. A consumer that + # pins this flake (rehosting/penguin#932) then SUBSTITUTES all 19 kernels + # instead of cross-building them in its own CI. + full: + name: full matrix + runs-on: rehosting-arc + needs: series + permissions: + contents: write + # Also on a push to main: with the Docker pipeline gone, this job is the + # ONLY thing that cuts a release, so a merge has to run the whole matrix. + if: >- + ${{ (github.event_name == 'workflow_dispatch' && inputs.full_matrix) + || startsWith(github.ref, 'refs/tags/nixdev_') + || (github.event_name == 'push' && github.ref == 'refs/heads/main') }} + steps: + - uses: actions/checkout@v4 + - name: Prepare runner directories + run: mkdir -p "$TMPDIR" "$XDG_CACHE_HOME" + - name: Set up Nix + uses: rehosting/ci/actions/nix-setup@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + cache-backend: cachix + cachix-name: rehosting-tools + cachix-auth-token: ${{ secrets.CACHIX_REHOSTING }} + # No VM needed; avoids install-nix-action's KVM/udevadm step, which + # fails on Arc pods that expose /dev/kvm without a working udevd. + enable-kvm: false + extra-nix-config: | + max-jobs = 8 + cores = 8 + + - name: Every kernel + run: nix build --print-out-paths ".#all" + + # Built separately from `.#all` so a perf or module failure is + # attributable at a glance rather than buried in a 19-cell aggregate. + - name: Every perf and module + run: | + set -euo pipefail + cells=$(nix eval --json ".#packages.x86_64-linux" --apply \ + 'p: builtins.filter (n: builtins.match "(perf|driver)-.*" n != null) + (builtins.attrNames p)' | python3 -c 'import json,sys;print("\n".join(json.load(sys.stdin)))') + fail=0 + for c in $cells; do + if nix build --no-link ".#packages.x86_64-linux.\"$c\""; then + echo "OK $c" + else + echo "FAIL $c"; fail=1 + fi + done + exit $fail + + # Every artifact's ELF class and byte order must match what its target + # name claims. Cheap, and it is the only check that catches a cell which + # builds cleanly and produces the wrong machine's binary. + - name: Shapes match target names + run: nix build --print-out-paths .#shape-check + + # Boot every cell. Slower than the rest of this job put together on some + # arches, and the only check that would have stopped nixdev_0.1.0's + # non-booting 4.10/x86_64 from being published. + - name: Every kernel boots + run: nix build --print-build-logs --print-out-paths .#boot-check + + # The config contract: does every cell END UP with the options IGLOO + # needs? Reads the post-olddefconfig .config, not the fragments, because + # olddefconfig silently drops options whose dependencies are unmet -- so + # a fragment saying CONFIG_MODVERSIONS=y proves nothing about what + # shipped. Sibling of the boot check: both exist for kernels that build + # and boot and then quietly do not do their job. + - name: Configs satisfy the IGLOO contract + run: nix build --print-build-logs --print-out-paths .#config-required + + # Advisory, so it must not fail the job -- but the output belongs in the + # log where a config change can be reviewed against it. + - name: Config redundancy report (advisory) + continue-on-error: true + run: | + out=$(nix build --no-link --print-out-paths .#config-redundant) + cat "$out" + + - name: Release tarballs assemble + run: | + set -euo pipefail + nix build --print-out-paths --out-link result-kernels .#kernels-latest + nix build --print-out-paths --out-link result-kernel-devel .#kernel-devel-all + # Copy out of the store: the outputs are read-only files whose store + # names already match, but the upload needs real files in the workdir. + cp -L result-kernels kernels-latest.tar.gz + cp -L result-kernel-devel kernel-devel-all.tar.gz + chmod u+w kernels-latest.tar.gz kernel-devel-all.tar.gz + ls -l kernels-latest.tar.gz kernel-devel-all.tar.gz + + # Manifest of what the nix path actually ships, in the log, so a reviewer + # can diff it against the Docker release without downloading anything. + - name: Manifest + run: | + tar tzf kernels-latest.tar.gz | sed 's|^\./||' | grep -v '/$' | sort + echo "--- perf coverage ---" + for v in 4.10 6.13; do + tot=$(tar tzf kernels-latest.tar.gz | grep -c "kernels/$v/osi\..*\.config" || true) + perf=$(tar tzf kernels-latest.tar.gz | grep -c "kernels/$v/perf\." || true) + echo " $v: perf for $perf of $tot targets" + done + + # Prerelease from a nixdev_* tag: the tag IS the version, no increment. + # Kept distinct from the vX.Y.Z release below so a downstream repo can be + # handed a fixed, immutable kernel set to test against without that + # consuming a version number. + - name: Publish prerelease + if: ${{ startsWith(github.ref, 'refs/tags/nixdev_') }} + uses: softprops/action-gh-release@v1 + with: + files: | + kernels-latest.tar.gz + kernel-devel-all.tar.gz + token: ${{ secrets.GITHUB_TOKEN }} + tag_name: ${{ github.ref_name }} + name: ${{ github.ref_name }} (nix, prerelease) + prerelease: true + body: | + Built by the **nix + patch series** path (linux_builder#59), not the + Docker pipeline. Prerelease, for downstream testing only -- it does + not participate in the `vX.Y.Z` version line. + + Differs from the Docker release in exactly two ways: + * `perf` for all 19 cells (the Docker path shipped 7 of 19) + * no `powerpcle` -- it was byte-identical to `powerpc` and + big-endian despite the name + + # ---- the vX.Y.Z version line ------------------------------------- + # Ported from the deleted build.yml. With the Docker pipeline gone this + # is the only thing that cuts a real release, so it lives here now; the + # collision hazard that kept the two apart no longer exists. + # + # `use_api: true` reads GIT TAGS via /git/matching-refs/tags/, takes the + # highest by `sort -V`, and applies `increment` (default: patch). So the + # way to move the version LINE is to push a bare marker tag at main's tip + # and let the patch increment continue from it -- v4.0.0 -> v4.0.1. + # + # Do NOT set `increment: minor` to force a jump. It is not self-clearing, + # so it silently bumps the release AFTER it as well, until someone + # remembers to revert. The marker tag has no such landmine. + - name: Get next version + id: version + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + uses: reecetech/version-increment@2023.10.1 + with: + use_api: true + + - name: Publish release + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + uses: softprops/action-gh-release@v1 + with: + files: | + kernels-latest.tar.gz + kernel-devel-all.tar.gz + token: ${{ secrets.GITHUB_TOKEN }} + tag_name: ${{ steps.version.outputs.v-version }} + name: Release ${{ steps.version.outputs.v-version }} + generate_release_notes: true + prerelease: false diff --git a/.gitignore b/.gitignore index 8915002..c991461 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ *.tar.gz *.log *.linted +cache cache/ kernels/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index c088407..0000000 --- a/.gitmodules +++ /dev/null @@ -1,8 +0,0 @@ -[submodule "linux_4.10"] - path = linux/4.10 - url = https://github.com/rehosting/linux.git - branch = main_4.10 -[submodule "linux_6.7"] - path = linux/6.13 - url = https://github.com/rehosting/linux.git - branch = main_6.7 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index a56fae9..0000000 --- a/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -ARG REGISTRY="docker.io" -ARG TARGET="latest" -FROM ${REGISTRY}/rehosting/embedded-toolchains:${TARGET} - -RUN apt-get update && apt-get install -y pkg-config - -# Get panda for kernelinfo_gdb. Definitely a bit overkill to pull the whole repo -RUN mkdir /extract_kernelinfo && \ - wget https://raw.githubusercontent.com/panda-re/panda-ng/refs/heads/main/plugins/osi_linux/utils/kernelinfo_gdb/extract_kernelinfo.py -O /extract_kernelinfo/extract_kernelinfo.py && \ - wget https://raw.githubusercontent.com/panda-re/panda-ng/refs/heads/main/plugins/osi_linux/utils/kernelinfo_gdb/run.sh -O /extract_kernelinfo/run.sh && \ - chmod +x /extract_kernelinfo/run.sh - diff --git a/README.md b/README.md index e1e1a0c..9749e24 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,193 @@ linux\_builder ==== +The IGLOO kernels: pristine upstream release tarballs plus an explicit IGLOO +patch series, cross-built by [kernelsmith][ks] for every target penguin +emulates. -Standalone repo for building a linux source tree with CI. This design keeps CI infrastructure, build scripts, and configs -out of the original tree making it easy to see the difference and port it to other kernel versions. +Two kernel versions (`4.10`, `6.13`) across up to eleven targets — 19 buildable +cells in all. Each cell ships a boot image, a `vmlinux`, a minimal kernel-devel +tree, `perf`, and the OSI/COSI analysis artifacts penguin needs. +[ks]: https://github.com/rehosting/kernelsmith -## Usage +## Quick start -### Update submodule +Everything is a Nix flake output. There is no container to build first. +```sh +nix build .#packages.x86_64-linux."kernel-6.13-armel" # one cell +nix build .#all # every buildable cell +nix build .#kernels # the payload as a tree +nix build .#kernels-latest # ...and as a tarball ``` -cd linux -# Checkout desired branch, pull, etc -git checkout master -git pull +Cell names contain a `.`, which Nix parses as an attribute-path separator, so +**the quoting above is mandatory** — `.#kernel-6.13-armel` does not resolve. -# Go back to project root -cd .. +## Where the source comes from -# Add new commit -git commit -am "Updated linux to ..." +Not from a submodule. `patches/` carries the IGLOO delta as an ordered series +applied to a pristine kernel.org tarball, pinned by hash in `patches/base.json`. +See [`patches/README.md`](patches/README.md) for the layout, how much is really +shared between versions, and how to add or refresh a patch. -git push +To prove a series still reproduces the fork branch it replaced: + +```sh +./scripts/verify-series.sh 4.10 ``` +`scripts/import-series.sh` and `scripts/export-series.sh` move patches between +this repo and a fork branch. + +## Checks + +| command | what it proves | +|---|---| +| `nix build .#boot-check` | **every kernel actually boots** under the qemu machine penguin runs it on | +| `nix build .#shape-check` | each artifact's ELF class, byte order and machine match its target name | +| `nix build .#config-required` | every kernel ended up with the options IGLOO needs (see [Modifying configs](#modifying-configs)) | +| `nix flake check` | the above, plus evaluation of every output | -### Make new release +`boot-check` is the one that matters most, and it is newer than the rest. +`nixdev_0.1.0` shipped a `4.10/x86_64` kernel that compiled, linked, packaged, +had the correct ELF shape and was the same size as the Docker build's — and +printed nothing before dying. Every other gate passed it; it was caught three +repos downstream in penguin's integration tests. Booting it is the only check +that would have said so. See [`nix/boot.nix`](nix/boot.nix). + +32-bit `powerpc` is boot-tested as an explicit SKIP: penguin's +`arch_registry.py` records no qemu machine for it, so it is unbootable by +declaration rather than by oversight. + +## Modifying configs -To make a new release, tag the repo with a `v*.*` string and push it: +Configs live in `configs//`, with shared fragments pulled in by +`#include` (`all-common.inc`, `arm-common.inc`, ...). They are assembled with +`cpp -P -undef` and then `olddefconfig`. + +**The file you edit is almost never the file an option comes from,** and the +config the kernel is *built* with is a third thing again — `olddefconfig` runs +last and drops anything whose dependencies are unmet. Three tools follow from +that, one per question: + +| question | command | +|---|---| +| Did every cell **end up** with what IGLOO needs? | `nix build .#config-required` | +| Where does `CONFIG_X` for this cell **come from**? | `nix run .#config-explain -- 6.13 x86_64 CONFIG_IGLOO` | +| Which lines am I writing that **do nothing**? | `nix build .#config-redundant` | + +`config-required` is a **gate**, and it reads the shipped `.config` rather than +the fragments — that distinction is the entire point. It asserts the options +whose absence is *silent*: a kernel that builds, boots, and then does not do +its job. `CONFIG_MODVERSIONS` is the sharpest of them; without it a mismatched +`igloo.ko` loads quietly instead of being rejected, which is strictly worse +than the mismatch. See `nix/config-tools.nix`, where every entry says what +breaks without it. + +`config-explain` builds no kernel, so it is instant. It exists because grep +does not answer the question: `CONFIG_IGLOO` is set in `all-common.inc` and no +target sets it directly, so grepping `configs/6.13/x86_64` finds nothing. ``` -git tag v3.1 -git push origin v3.1 +$ nix run .#config-explain -- 6.13 armel CONFIG_MODULES + CONFIG_MODULES=y + configs/6.13/all-common.inc:159 + via armel -> arm-common.inc -> all-common.inc +* CONFIG_MODULES=y + configs/6.13/all-common.inc:162 + via armel -> arm-common.inc -> all-common.inc + + 2 assignments; the one marked * wins (cpp: last wins). ``` -The CI jobs will then build your release and make it available at `https://github.com/panda-re/linux_builder/releases/download//kernels-latest.tar.gz` +`config-redundant` separates two things that look alike and are not: an option +assigned **twice** (always a bug — only the last has effect) and an option that +**did not survive olddefconfig** (usually a dependency you did not notice). It +currently reports 595 duplicate assignments across the matrix. -## Modifying configs -If you'd like to add a given option or set of options to all configs, you can add it to [configs/all-common.inc](configs/all-common.inc). +And the raw savedefconfig lint, per cell or across the matrix: + +```sh +nix build .#packages.x86_64-linux."config-lint-6.13-armel" # one cell +nix build .#config-lint # every cell +``` + +Each produces `config__.linted` (the `savedefconfig` form) and +a `.diff` against the assembled config. **This is advisory, not a gate.** +`savedefconfig` prunes options that are already the arch default and +de-duplicates, so a readable config fragment and its savedefconfig form never +match — the diff tells you which of your lines were redundant, and that is all +it is for. Turning it into an assertion would mean replacing the `#include` +structure with savedefconfig output, which is a bad trade. + +This replaces `./build.sh --config-only`, which did the same thing and likewise +discarded its own exit status. + +## Releases + +There are two, and they are deliberately separate. + +**`vX.Y.Z` — the real version line.** Cut automatically on every merge to +`main`. The full matrix runs, boot-tests, and publishes `kernels-latest.tar.gz` ++ `kernel-devel-all.tar.gz`. The version comes from +`reecetech/version-increment` with `use_api: true`, which reads **git tags**, +takes the highest by `sort -V`, and bumps the patch. + +To move the version *line* (say 3.6.x → 4.0.x), **push a bare marker tag at +main's tip** and let the patch increment continue from it: + +```sh +git tag v4.0.0 && git push origin v4.0.0 # marker only, no release +# the next merge to main then cuts v4.0.1 +``` + +Do **not** set `increment: minor` in the workflow to force a jump. It is not +self-clearing, so it silently bumps the release *after* it as well, until +someone remembers to revert. The marker tag has no such landmine. + +**`nixdev_*` — prereleases, off the version line.** The tag *is* the version; +no increment. For handing a downstream repo a fixed, immutable kernel set to +test against without consuming a version number. + +```sh +git tag -a nixdev_0.1.2 -m "..." && git push origin nixdev_0.1.2 +``` -## Linting configs -Before commiting any config update you MUST lint your change by running `./build.sh --config-only`. You can specify architectures to lint with the `--targets` flag, e.g., `./build.sh --config-only --targets "armel mipel"`. If the lint fails, build.sh will exit with non-zero status and print a diff containing the problems. +Both run the full matrix, and both are `push` events — so `nix-setup`'s +skip-push default does not apply and every cell lands in the `rehosting-tools` +Cachix. That is what lets downstream repos *substitute* these kernels instead +of cross-building all 19 in their own CI. + +## Consumers + +- **penguin** takes this repo as a *flake input* and stages `.#kernels` + directly. The tarball is kept for other consumers, but the flake is the seam + that records **which compiler built these kernels** — a tarball pin cannot. +- **igloo_driver** builds `igloo.ko` against the kernel *derivations* here, so + a mismatched (kernel, module) pair is not expressible. Note that a nix-built + `kernel-devel-all.tar.gz` is **not** usable from a non-nix container: it ships + host tools linked against `/nix/store`. + +## History: the Docker path + +This repo used to build inside `rehosting/embedded-toolchains` via `build.sh`, +`_in_container_build.sh` and a `Dockerfile`, with the kernel source arriving as +two git submodules. All of that is removed; it lives in git history. + +Comments throughout `nix/` name `_in_container_build.sh` where the Nix code is a +faithful port of a specific step in it. Those references are deliberate +provenance — they say *why* a piece of the build looks the way it does — and +point at the file as it existed before its removal. + +Two things worth knowing about what that path did, because both were silent: + +- Its toolchains were unversioned `wget musl.cc/*-cross.tgz` downloads, so + **every kernel this repo has ever released has no recorded compiler + identity.** kernelsmith exists to fix that. +- It hard-coded a hand-built `x86_64-legacy` toolchain for `(x86_64, 4.10)` + alone, pinning binutils 2.30. The reason was never written down. It is that + binutils ≥ 2.31 emits `R_X86_64_PLT32`, which Linux only learned in 4.16 — + and that omission is exactly what shipped a dead kernel in `nixdev_0.1.0`. + It is now a resolver decision in kernelsmith with the reason attached. diff --git a/_in_container_build.sh b/_in_container_build.sh deleted file mode 100755 index 9e380e7..0000000 --- a/_in_container_build.sh +++ /dev/null @@ -1,409 +0,0 @@ -#!/bin/bash - -set -eu - -# Re-own bind-mounted outputs to the host caller so artifacts/cache aren't left -# root-owned on the host. Runs as root inside the container via an EXIT trap so -# it covers every exit path (including the early kernel-devel exit). Gated on a -# non-root HOST_UID, so CI (which may run as root / not set these) is unaffected. -on_exit() { - # ccache summary on every exit path (there are several early exits: - # config-only, menuconfig, diffdefconfig, kernel-devel). No-op unless ccache - # is enabled. - if [ -n "${CCACHE_DIR:-}" ]; then - echo "ccache stats after build:" - ccache -s 2>/dev/null | sed 's/^/ ccache(end): /' || true - fi - if [ -n "${HOST_UID:-}" ] && [ "${HOST_UID}" != "0" ]; then - chown -R "${HOST_UID}:${HOST_GID:-$HOST_UID}" \ - /tmp/build \ - /app/kernels-latest.tar.gz \ - /app/kernel-devel-all.tar.gz 2>/dev/null || true - fi -} -trap on_exit EXIT - -# We want to build linux for each of our targets and versions using the config files. Linux is in /app/linux/[version] -# while our configs are at configs/[version]/[arch]. We need to set the ARCH and CROSS_COMPILE variables -# and put the binaries in /app/binaries - -# Get options from build.sh -CONFIG_ONLY="$1" -VERSIONS="$2" -TARGETS="$3" -NO_STRIP="$4" -MENU_CONFIG="$5" -DIFFDEFCONFIG="$6" -KERNEL_DEVEL="${7:-false}" - -echo "Config only: $CONFIG_ONLY" -echo "Versions: $VERSIONS" -echo "Targets: $TARGETS" -echo "No strip: $NO_STRIP" -echo "menuconfig: $MENU_CONFIG" -echo "diffdefconfig: $DIFFDEFCONFIG" - -# Optional compiler cache. Enabled purely by the presence of CCACHE_DIR (set by -# the caller, e.g. CI mounts a node-shared persistent dir). When unset, CC_PREFIX -# stays empty and every `CC=` below expands to exactly the kernel default -# ($(CROSS_COMPILE)gcc), so behaviour is identical to before. ccache is content- -# keyed, so it survives `mrproper`/clean and stays correct across config and -# source bumps; concurrent build jobs sharing one CCACHE_DIR are safe (ccache -# locks internally). The container paths ($O=/tmp/build/..., src /app/linux) are -# stable across runs, which is what lets objects hit. -CC_PREFIX="" -if [ -n "${CCACHE_DIR:-}" ]; then - mkdir -p "$CCACHE_DIR" - export CCACHE_DIR - export CCACHE_MAXSIZE="${CCACHE_MAXSIZE:-15G}" - export CCACHE_COMPRESS=1 - # __DATE__/__TIME__ and header mtimes churn across a fresh checkout; treat - # them as sloppy so unchanged translation units still hit. - export CCACHE_SLOPPINESS="${CCACHE_SLOPPINESS:-time_macros,include_file_mtime,include_file_ctime,file_stat_matches}" - ccache -M "$CCACHE_MAXSIZE" >/dev/null 2>&1 || true - CC_PREFIX="ccache " - echo "ccache enabled: dir=$CCACHE_DIR maxsize=$CCACHE_MAXSIZE" - ccache -s 2>/dev/null | sed 's/^/ ccache(start): /' || true -fi - -# Array to keep track of child processes -declare -a pids - -# Function to get cross-compiler prefix -get_cc() { - local arch=$1 - local version=$2 - local abi="" - - # Clear only CFLAGS; do not clear KCFLAGS or KBUILD_CFLAGS here - unset CFLAGS - - if [[ $arch == *"arm64"* ]]; then - abi="" - arch="aarch64" - elif [[ $arch == *"arm"* ]]; then - abi="eabi" - if [[ $arch == *"eb"* ]]; then - export CFLAGS="-mbig-endian" - export KCFLAGS="${KCFLAGS:-} -mbig-endian" - fi - arch="arm" - fi - - if [[ $arch == *"loongarch"* ]]; then - echo "/opt/cross/loongarch64-linux-gcc-cross/bin/loongarch64-unknown-linux-gnu-" - elif [[ $arch == *"powerpc"* ]] && [ "$version" = "4.10" ]; then - echo "powerpc64-linux-gnu-" - elif [[ $arch == *"powerpc"* ]] && [ "$version" != "4.10" ]; then - echo "/opt/cross/powerpc64-linux-musl-cross/bin/powerpc64-linux-musl-" - elif [ "$arch" = "x86_64" ] && [ "$version" = "4.10" ]; then - echo "/opt/cross/x86_64-legacy/bin/x86_64-linux-musl-" - elif [[ $arch == "riscv64" ]]; then - # riscv64 linux-musl seems to run out of memory on linking so we switched - # to the glibc version - echo "/usr/bin/riscv64-linux-gnu-" - else - echo "/opt/cross/${arch}-linux-musl${abi}/bin/${arch}-linux-musl${abi}-" - fi -} - -for VERSION in $VERSIONS; do -make -C /app/linux/$VERSION mrproper -for TARGET in $TARGETS; do - unset KCFLAGS KBUILD_CFLAGS HOSTCFLAGS - BUILD_TARGETS="vmlinux" - if [ $TARGET == "armel" ]; then - BUILD_TARGETS="vmlinux zImage" - elif [ $TARGET == "arm64" ]; then - BUILD_TARGETS="vmlinux Image.gz" - elif [ $TARGET == "x86_64" ]; then - BUILD_TARGETS="vmlinux bzImage" - elif [ $TARGET == "loongarch64" ]; then - BUILD_TARGETS="vmlinux vmlinuz.efi" - elif [ $TARGET == "riscv32" ]; then - BUILD_TARGETS="vmlinux Image" - elif [ $TARGET == "riscv64" ]; then - BUILD_TARGETS="vmlinux Image" - fi - - # Set short_arch based on TARGET - short_arch=$(echo $TARGET | sed -E 's/(.*)(e[lb]|eb64)$/\1/') - if [ "$short_arch" == "mips64" ]; then - short_arch="mips" - elif [ "$short_arch" == "loongarch64" ]; then - short_arch="loongarch" - elif [[ "$short_arch" == "powerpc64" || "$short_arch" == "powerpc64le" || "$short_arch" == "powerpcle" ]]; then - short_arch="powerpc" - elif [ "$short_arch" == "riscv64" ]; then - short_arch="riscv" - elif [ "$short_arch" == "riscv32" ]; then - short_arch="riscv" - fi - - # Apply extra warning suppressions only for old kernels - if [[ "$VERSION" == "4.10" && "$TARGET" == powerpc* ]]; then - echo "Applying global Wno-error for $VERSION $TARGET" - extra_wnoerr="-Wno-error -Wno-error=stringop-truncation -Wno-error=format-truncation -Wno-error=maybe-uninitialized -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=missing-attributes" - export KCFLAGS="${KCFLAGS:-} $extra_wnoerr" - export KBUILD_CFLAGS="${KBUILD_CFLAGS:-} $extra_wnoerr" - export HOSTCFLAGS="${HOSTCFLAGS:-} -Wno-error" - fi - - echo "Building $BUILD_TARGETS for $TARGET" - - if [ ! -f "/app/configs/${VERSION}/${TARGET}" ]; then - echo "No config for $TARGET" avaiable for version $VERSION. - # Only exit if there is a single version being built - if [ "$(echo $VERSIONS | wc -w)" -eq 1 ]; then - echo "Since only one version is being built, exiting." - exit 1 - fi - echo "Assuming this is fine in multi-version builds, skipping." - continue - fi - mkdir -p "/tmp/build/${VERSION}/${TARGET}" - cpp -P -undef "/app/configs/${VERSION}/${TARGET}" -o "/tmp/build/${VERSION}/${TARGET}/.config" - - - # If updating configs, lint them with kernel first! This removes default options and duplicates. - if $CONFIG_ONLY; then - echo "Linting config for $TARGET to config_${VERSION}_${TARGET}.linted" - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) O=/tmp/build/${VERSION}/${TARGET}/ savedefconfig - cp "/tmp/build/${VERSION}/${TARGET}/defconfig" "/app/config_${VERSION}_${TARGET}.linted" - # Informational only: savedefconfig prunes defaults/dupes so the configs - # always differ. Don't let the non-zero exit abort the (set -e) loop, or - # only the first target ever gets linted in a multi-target --config-only run. - diff -u <(sort /tmp/build/${VERSION}/${TARGET}/.config) <(sort /tmp/build/${VERSION}/${TARGET}/defconfig | sed '/^[ #]/d') || true - else - echo "Building kernel for $TARGET" - # 2. Inject missing label for x86_64 R_X86_64_PLT32 backport bug - if [[ "$VERSION" == "4.10" && "$TARGET" == "x86_64" ]]; then - grep -q "invalid_relocation:" /app/linux/$VERSION/arch/x86/kernel/module.c || \ - sed -i 's/overflow:/invalid_relocation:\n\treturn -ENOEXEC;\noverflow:/g' /app/linux/$VERSION/arch/x86/kernel/module.c - fi - - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) O=/tmp/build/${VERSION}/${TARGET}/ olddefconfig - if $MENU_CONFIG; then - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) O=/tmp/build/${VERSION}/${TARGET}/ menuconfig - exit - elif $DIFFDEFCONFIG; then - cp /tmp/build/${VERSION}/${TARGET}/.config /tmp/original_config - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) O=/tmp/build/${VERSION}/${TARGET}/ defconfig - /app/linux/${VERSION}/scripts/diffconfig /tmp/original_config /tmp/build/${VERSION}/${TARGET}/.config - exit - fi - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) CC="${CC_PREFIX}$(get_cc $TARGET $VERSION)gcc" O=/tmp/build/${VERSION}/${TARGET}/ $BUILD_TARGETS -j$(nproc) - - # Always run modules_prepare to ensure headers and Module.symvers are generated - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) CC="${CC_PREFIX}$(get_cc $TARGET $VERSION)gcc" O=/tmp/build/${VERSION}/${TARGET}/ modules_prepare - - # Build modules to ensure Module.symvers is generated - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) CC="${CC_PREFIX}$(get_cc $TARGET $VERSION)gcc" O=/tmp/build/${VERSION}/${TARGET}/ modules -j$(nproc) - - # Prepare and completely clean the output directory for perf - PERF_OUTDIR="/tmp/build/${VERSION}/${TARGET}/tools/perf/" - rm -rf "$PERF_OUTDIR" - mkdir -p "$PERF_OUTDIR" - - # 3. Prepare specific Linker flags for MIPS64 ABI mismatches - PERF_LD="$(get_cc $TARGET $VERSION)ld" - if [[ "$TARGET" == *"mips64el"* ]]; then - PERF_LD="${PERF_LD} -m elf64ltsmip" - elif [[ "$TARGET" == *"mips64eb"* || "$TARGET" == "mips64" ]]; then - PERF_LD="${PERF_LD} -m elf64btsmip" - fi - - # Build perf utility statically - make -C /app/linux/$VERSION/tools/perf \ - ARCH=${short_arch} \ - CROSS_COMPILE=$(get_cc $TARGET $VERSION) \ - CC="${CC_PREFIX}$(get_cc $TARGET $VERSION)gcc" \ - LD="$PERF_LD" \ - OUTPUT="$PERF_OUTDIR" \ - LDFLAGS="-static" \ - WERROR=0 \ - EXTRA_CFLAGS="-Wno-error -fcommon -D__always_inline=inline -Wno-redundant-decls -Wno-format-truncation -Wno-format-overflow -Wno-array-bounds" \ - HOSTCFLAGS="-Wno-error" \ - NO_LIBELF=1 NO_LIBUNWIND=1 NO_LIBNUMA=1 NO_LIBAUDIT=1 \ - NO_LIBBIONIC=1 NO_LIBPYTHON=1 NO_LIBPERL=1 NO_SLANG=1 NO_LZMA=1 \ - NO_ZLIB=1 NO_LIBBPF=1 NO_JVMTI=1 NO_LIBCRYPTO=1 NO_LIBZSTD=1 \ - NO_LIBTRACEEVENT=1 NO_AUXTRACE=1 NO_CORESIGHT=1 \ - -j$(nproc) || echo "Warning: Failed to build perf for $TARGET ($VERSION)" - mkdir -p /kernels/$VERSION - - # Copy perf to delivery directory - PERF_SRC="${PERF_OUTDIR}perf" - if [ -f "$PERF_SRC" ]; then - cp "$PERF_SRC" "/kernels/$VERSION/perf.${TARGET}" - if ! $NO_STRIP; then - $(get_cc $TARGET $VERSION)strip "/kernels/$VERSION/perf.${TARGET}" || true - fi - fi - - # Copy only the required boot artifact per architecture - BOOT_SRC="" - BOOT_DST="" - case "$TARGET" in - armel) - BOOT_SRC="arch/arm/boot/zImage"; BOOT_DST="zImage.${TARGET}" ;; - arm64) - BOOT_SRC="arch/arm64/boot/Image.gz"; BOOT_DST="zImage.${TARGET}" ;; - x86_64) - BOOT_SRC="arch/x86/boot/bzImage"; BOOT_DST="bzImage.${TARGET}" ;; - loongarch64) - BOOT_SRC="arch/loongarch/boot/vmlinuz.efi"; BOOT_DST="vmlinuz.efi.${TARGET}" ;; - riscv64|riscv32) - BOOT_SRC="arch/riscv/boot/Image"; BOOT_DST="Image.${TARGET}" ;; - *) - BOOT_SRC=""; BOOT_DST="" ;; - esac - if [ -n "$BOOT_SRC" ]; then - if [ -f "/tmp/build/${VERSION}/${TARGET}/${BOOT_SRC}" ]; then - cp "/tmp/build/${VERSION}/${TARGET}/${BOOT_SRC}" "/kernels/$VERSION/${BOOT_DST}" - else - echo "Warning: Expected boot artifact not found: ${BOOT_SRC} for ${TARGET}" - fi - fi - - # vmlinux is needed for analysis, but only shipped if it is the deliverable (mips*/powerpc*) - VMLINUX_SRC="/tmp/build/${VERSION}/${TARGET}/vmlinux" - DELIVER_VMLINUX=false - case "$TARGET" in - mips*|powerpc*|powerpcle|powerpc64*|powerpc64le) - DELIVER_VMLINUX=true - ;; - esac - - # Launch kernel processing in subprocess - time ( - # Generate OSI/COSI from build-tree vmlinux - echo "[${TARGET}]" >> /kernels/$VERSION/osi.${TARGET}.config - /extract_kernelinfo/run.sh \ - "${VMLINUX_SRC}" /tmp/panda_profile.${TARGET} - cat /tmp/panda_profile.${TARGET} >> /kernels/$VERSION/osi.${TARGET}.config - dwarf2json linux --elf "${VMLINUX_SRC}" | xz -c > /kernels/$VERSION/cosi.${TARGET}.json.xz - - # If vmlinux is the boot artifact for this TARGET, copy and (optionally) strip it - if $DELIVER_VMLINUX; then - cp "${VMLINUX_SRC}" "/kernels/$VERSION/vmlinux.${TARGET}" - if ! $NO_STRIP; then - $(get_cc $TARGET $VERSION)strip "/kernels/$VERSION/vmlinux.${TARGET}" - fi - fi - - echo "Completed processing for $TARGET ($VERSION)" - ) & - # Store the PID of the background process - pids+=($!) - - # Create minimal kernel-devel archive for module builds - ( - KBUILD_DIR="/tmp/build/${VERSION}/${TARGET}" - KERNEL_SRC="/app/linux/${VERSION}" - OUTDIR="/minimal-devel/${TARGET}.${VERSION}" - mkdir -p "$OUTDIR" - # Explicitly copy .config file - if [ -f "$KBUILD_DIR/.config" ]; then - cp "$KBUILD_DIR/.config" "$OUTDIR/.config" - fi - cp "$KBUILD_DIR/Module.symvers" "$OUTDIR/" || true - cp -r "$KERNEL_SRC/include" "$OUTDIR/" || true - cp -r "$KBUILD_DIR/include" "$OUTDIR/" || true - mkdir -p "$OUTDIR/arch/${short_arch}" - cp -r "$KERNEL_SRC/arch/${short_arch}" "$OUTDIR/arch/" || true - cp -r "$KBUILD_DIR/arch/${short_arch}" "$OUTDIR/arch/" || true - # Remove arch/${ARCH}/boot from OUTDIR - rm -rf "$OUTDIR/arch/${short_arch}/boot" || true - if [ $short_arch == "x86_64" ]; then - # MIPS has a different arch directory structure - mkdir -p "$OUTDIR/arch/x86" - cp -r "$KERNEL_SRC/arch/x86" "$OUTDIR/arch/" || true - cp -r "$KBUILD_DIR/arch/x86" "$OUTDIR/arch/" || true - fi - cp -r "$KERNEL_SRC/scripts" "$OUTDIR/" || true - cp -r "$KBUILD_DIR/scripts" "$OUTDIR/" || true - cp -r "$KERNEL_SRC/tools" "$OUTDIR/" || true - cp -r "$KBUILD_DIR/tools" "$OUTDIR/" || true - cp "$KERNEL_SRC/Makefile" "$OUTDIR/" || true - cp "$KERNEL_SRC/Kconfig" "$OUTDIR/" || true - # Ensure fixdep is present for out-of-tree module builds - cp -r "$KBUILD_DIR/scripts/" "$OUTDIR/scripts/" || true - - # --- Slim the staged devel tree ------------------------------------- - # An out-of-tree module build (make -C $KDIR M=$PWD modules) only needs - # the modules_prepare result: Makefile/.config/Module.symvers, headers - # (include/, arch//include), arch Makefiles, and scripts/ host - # tools. It does not read boot images, prebuilt build objects, or most - # of tools/, so drop them -- this cuts the per-target devel archive - # ~75-85% (e.g. x86_64 ~605MB -> ~100MB uncompressed). - # - # NOTE: the `rm -rf "$OUTDIR/arch/${short_arch}/boot"` above is a no-op - # for x86_64 (real arch dir is arch/x86, but short_arch is "x86_64"), so - # the full arch/x86/boot (~120MB of bzImage/vmlinux) used to ship. The - # arch/*/boot glob here removes it properly for every arch. - rm -rf "$OUTDIR"/arch/*/boot "$OUTDIR"/arch/*/realmode || true - # Keep tools/objtool (kbuild may run it on module objects when - # CONFIG_OBJTOOL=y); drop the rest of tools/ (perf, testing, bpf = bulk). - if [ -d "$OUTDIR/tools" ]; then - find "$OUTDIR/tools" -mindepth 1 -maxdepth 1 ! -name objtool -exec rm -rf {} + || true - fi - # Drop build leftovers. Keep arch/powerpc/lib/crtsavres.o (igloo_driver - # links it for ppc targets) and everything under scripts/ and tools/ - # (host tools needed for the external-module build). - find "$OUTDIR" -name '*.cmd' -delete || true - find "$OUTDIR" -name '*.o' \ - ! -path '*/arch/powerpc/lib/crtsavres.o' \ - ! -path '*/scripts/*' \ - ! -path '*/tools/*' -delete || true - # Drop kernel .c source too: an `M=` external-module build compiles the - # module's own sources against prebuilt objects + headers, never the - # in-tree .c. Keep scripts/ and tools/ sources in case a host tool needs - # a rebuild. - find "$OUTDIR" -name '*.c' \ - ! -path '*/scripts/*' \ - ! -path '*/tools/*' -delete || true - ) & - - # Store the PID of the background process - pids+=($!) - echo "Started background process ${pids[-1]} for $TARGET ($VERSION)" - fi -done -done - -if ! $CONFIG_ONLY; then - echo "Waiting for all kernel processing to complete..." - # Wait for all background processes to complete - for pid in "${pids[@]}"; do - wait $pid - echo "Process $pid completed" - done - for VERSION in $VERSIONS; do - # Only concatenate if the version directory exists and there are any osi.*.config files - if [ -d "/kernels/$VERSION" ]; then - shopt -s nullglob - osi_configs=(/kernels/$VERSION/osi.*.config) - if [ ${#osi_configs[@]} -gt 0 ]; then - cat "${osi_configs[@]}" >> /kernels/$VERSION/osi.config - fi - shopt -u nullglob - else - echo "Skipping osi.config aggregation for $VERSION (no /kernels/$VERSION directory)" - fi - done - echo "All processes completed, creating final archive" - echo "Built by linux_builder on $(date)" > /kernels/README.txt - tar cvf - /kernels | pigz > /app/kernels-latest.tar.gz - chmod o+rw /app/kernels-latest.tar.gz -fi - -if [ "$KERNEL_DEVEL" = "true" ]; then - echo "Aggregating all kernel-devel artifacts into kernel-devel-all.tar.gz..." - - # Create the tar directly from the minimal-devel directory using pigz for parallel compression - tar cf - -C /minimal-devel . | pigz > /app/kernel-devel-all.tar.gz - exit 0 -fi - -# Ensure cache can be read/written by host -chmod -R o+rw /tmp/build diff --git a/build.sh b/build.sh deleted file mode 100755 index 3b844ac..0000000 --- a/build.sh +++ /dev/null @@ -1,181 +0,0 @@ -#!/bin/bash - -set -eux - -help() { - cat >&2 < auto-detect all version directories under ./linux -TARGETS="armel arm64 mipseb mipsel mips64eb mips64el powerpc powerpcle powerpc64 powerpc64le loongarch64 riscv64 x86_64" -NO_STRIP=false -MENU_CONFIG=false -INTERACTIVE= -DIFFDEFCONFIG=false -KERNEL_DEVEL=true -IMAGE="rehosting/linux_builder" -EXTRA_DOCKER_OPTS="" -CACHE_DIR="cache" -CLEAR_CACHE=false - -# Parse command-line arguments -while [[ $# -gt 0 ]]; do - case "$1" in - --help) - help - exit - ;; - --clear-cache) - CLEAR_CACHE=true - shift - ;; - --config-only) - CONFIG_ONLY=true - shift - ;; - --versions) - VERSIONS="$2" - shift # past flag - shift # past value - ;; - --no-strip) - NO_STRIP=true - shift # past flag - ;; - --menuconfig) - MENU_CONFIG=true - INTERACTIVE=-it - shift # past flag - ;; - --targets) - TARGETS="$2" - shift # past flag - shift # past value - ;; - --diffdefconfig) - DIFFDEFCONFIG=true - shift - ;; - --kernel-devel) - KERNEL_DEVEL=true - shift - ;; - --image) - IMAGE="$2" - shift # past flag - shift # past value - ;; - --extra-docker-opts) - EXTRA_DOCKER_OPTS="$2" - shift # past flag - shift # past value - ;; - --cache-dir) - CACHE_DIR="$2" - shift # past flag - shift # past value - ;; - *) - help - exit 1 - ;; - esac -done - -# Auto-detect versions if not provided -if [[ -z "${VERSIONS// }" ]]; then - if [[ ! -d linux ]]; then - echo "Error: linux directory not found; cannot auto-detect versions. Use --versions." >&2 - exit 1 - fi - mapfile -t _version_dirs < <(find linux -maxdepth 1 -mindepth 1 -type d -printf '%f\n' | sort -V) - if [[ ${#_version_dirs[@]} -eq 0 ]]; then - echo "Error: No version subdirectories found under linux/. Use --versions." >&2 - exit 1 - fi - VERSIONS="${_version_dirs[*]}" -fi - -# Resolve host cache directory path: -if [[ "$CACHE_DIR" == "cache" ]]; then - CACHE_HOST_DIR="$PWD/cache" -else - CACHE_HOST_DIR="$CACHE_DIR" -fi - -# Bind-mount source translation for a shared Docker daemon (opt-in, pure -# no-op otherwise). -# -# Normally dockerd shares this script's mount namespace, so a bind-mount -# source path ("$PWD", the cache dir) means the same thing to the daemon as -# to us. Under a shared per-node daemon (e.g. rehosting CI's shared-docker -# runners) it does NOT: the daemon can't see this runner's per-pod workspace -# under /home/runner/_work, so "-v $PWD:/app" would mount an empty dir and the -# build would fail with "/app/_in_container_build.sh: No such file". The runner -# exports PENGUIN_HOST_MOUNT_FROM / PENGUIN_HOST_MOUNT_TO (the same mechanism -# penguin's wrapper uses) giving the daemon-visible location of that workspace. -# -# rewrite_mount() rewrites a bind source ONLY when BOTH env vars are set and -# the path is under _FROM. When they're unset — every local build and every -# GitHub-hosted runner — it returns the path unchanged, so behaviour is -# identical to before. (Sources outside _FROM, e.g. the kernel-source cache -# under /home/runner/_shared, are left as-is; the shared daemon mirrors that -# node-shared path directly.) -rewrite_mount() { - local path="$1" - if [[ -n "${PENGUIN_HOST_MOUNT_FROM:-}" && -n "${PENGUIN_HOST_MOUNT_TO:-}" \ - && "$path" == "${PENGUIN_HOST_MOUNT_FROM}"* ]]; then - printf '%s' "${PENGUIN_HOST_MOUNT_TO}${path#"$PENGUIN_HOST_MOUNT_FROM"}" - else - printf '%s' "$path" - fi -} -CACHE_MOUNT_SRC="$(rewrite_mount "$CACHE_HOST_DIR")" -APP_MOUNT_SRC="$(rewrite_mount "$PWD")" - -if $CLEAR_CACHE; then - docker run --rm -v "$CACHE_MOUNT_SRC":/tmp/build -v "$APP_MOUNT_SRC":/app pandare/kernel_builder /bin/bash -c "rm -rf /tmp/build/*" - exit -fi - -# Check if the image exists locally, build if not -if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then - echo "Docker image $IMAGE not found, building it..." - docker build -t "$IMAGE" . -fi - -mkdir -p "$CACHE_HOST_DIR" - -docker run $INTERACTIVE \ - --rm -v "$CACHE_MOUNT_SRC":/tmp/build \ - -v "$APP_MOUNT_SRC":/app \ - -e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" \ - $EXTRA_DOCKER_OPTS \ - "$IMAGE" \ - bash /app/_in_container_build.sh \ - "$CONFIG_ONLY" "$VERSIONS" "$TARGETS" \ - "$NO_STRIP" "$MENU_CONFIG" "$DIFFDEFCONFIG" "$KERNEL_DEVEL" \ No newline at end of file diff --git a/configs/6.13/powerpcle b/configs/6.13/powerpcle deleted file mode 100644 index d1e1d32..0000000 --- a/configs/6.13/powerpcle +++ /dev/null @@ -1,2 +0,0 @@ -#include "powerpc" -CONFIG_CPU_LITTLE_ENDIAN=y diff --git a/configs/6.13/powerpcle.unused b/configs/6.13/powerpcle.unused new file mode 100644 index 0000000..06b5d2b --- /dev/null +++ b/configs/6.13/powerpcle.unused @@ -0,0 +1,33 @@ +# RETIRED. A 32-bit little-endian powerpc kernel cannot be built from mainline +# Linux, so this config never produced what its name claims. +# +# arch/powerpc/platforms/Kconfig.cputype: +# +# config CPU_LITTLE_ENDIAN +# bool "Build little endian kernel" +# depends on PPC_BOOK3S_64 +# +# PPC_BOOK3S_64 is 64-bit only, so on a 32-bit config the symbol is not +# selectable, olddefconfig drops the line below without a word, and the build +# falls back to big endian. The resulting vmlinux.powerpcle was verified +# BYTE-IDENTICAL (sha256) to vmlinux.powerpc. +# +# The cell therefore cost a kernel build, a static perf build, an osi + cosi +# extraction and an igloo.ko build per release, to ship `powerpc` a second time +# under a name asserting the opposite. Worse than the waste: had penguin ever +# selected powerpcle for genuinely little-endian firmware, it would have been +# handed a big-endian kernel with nothing to indicate it. (In practice powerpc +# LE hardware starts at POWER8/ppc64le, so the likely device impact is zero -- +# but that is luck, not design.) +# +# 4.10 already retired this target (configs/4.10/powerpcle.unused); this brings +# 6.13 into line. If a real 32-bit LE powerpc target ever appears, it needs +# kernel support that does not exist upstream, not this file. +# +# `nix build .#shape-check` now asserts every cell's ELF class and byte order +# against its target name, so a future config asking for an endianness Kconfig +# will not grant fails loudly instead of silently. +# +# Original contents: +# #include "powerpc" +# CONFIG_CPU_LITTLE_ENDIAN=y diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..92af1ca --- /dev/null +++ b/flake.lock @@ -0,0 +1,100 @@ +{ + "nodes": { + "igloo_driver": { + "flake": false, + "locked": { + "lastModified": 1786551192, + "narHash": "sha256-zeOP0nQEb/uns46MizbcX3DpS4or4EM8XuOmRRIPbo4=", + "owner": "rehosting", + "repo": "igloo_driver", + "rev": "243c889e2b3360b16e5b12d6a20b0c5c0766dd07", + "type": "github" + }, + "original": { + "owner": "rehosting", + "repo": "igloo_driver", + "type": "github" + } + }, + "kernelsmith": { + "inputs": { + "musl-cross-make": "musl-cross-make", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1786744385, + "narHash": "sha256-linrkYJ63mdakHdQStEPgUeII3Tn/NGKFBQVCs3QB0w=", + "owner": "rehosting", + "repo": "kernelsmith", + "rev": "03d99e438ecab7a6ffb4d2a3072ef78269b2573c", + "type": "github" + }, + "original": { + "owner": "rehosting", + "repo": "kernelsmith", + "type": "github" + } + }, + "musl-cross-make": { + "flake": false, + "locked": { + "lastModified": 1781646546, + "narHash": "sha256-ZGkLvkred/sDa2Mw2/1N+Nh8bvUyujxxOLTZDfd2qN4=", + "owner": "richfelker", + "repo": "musl-cross-make", + "rev": "227df8b99103f9c59f6570babf892978e293082f", + "type": "github" + }, + "original": { + "owner": "richfelker", + "repo": "musl-cross-make", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1735563628, + "narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-qemu": { + "locked": { + "lastModified": 1767313136, + "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "igloo_driver": "igloo_driver", + "kernelsmith": "kernelsmith", + "nixpkgs": [ + "kernelsmith", + "nixpkgs" + ], + "nixpkgs-qemu": "nixpkgs-qemu" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..4d62ef7 --- /dev/null +++ b/flake.nix @@ -0,0 +1,266 @@ +{ + description = "IGLOO kernels: pristine upstream tarball + IGLOO patch series, cross-built by kernelsmith"; + + nixConfig = { + extra-substituters = [ "https://rehosting-tools.cachix.org" ]; + extra-trusted-public-keys = [ + "rehosting-tools.cachix.org-1:iNKSaFwG7MfGn6Fk7oTmIcLHqfffQ+cQIE5gWc6MlY0=" + ]; + }; + + inputs = { + # Provides the (kernel version, arch) -> cross toolchain resolver. Replaces + # the embedded-toolchains Docker image, whose toolchains were unversioned + # `wget https://musl.cc/*-cross.tgz` downloads -- i.e. today's shipped + # kernels have no recorded compiler identity. + kernelsmith.url = "github:rehosting/kernelsmith"; + nixpkgs.follows = "kernelsmith/nixpkgs"; + + # qemu ONLY, for nix/boot.nix. Deliberately not `nixpkgs`: the kernel build + # pins 24.05 (qemu 8.2.7), which predates nixpkgs shipping + # edk2-loongarch64-code.fd -- and loongarch64's kernel_fmt is vmlinuz.efi, + # a PE image that needs EFI firmware to boot at all. Pinning the harness's + # qemu separately also stops "what the kernel builds against" and "what we + # boot on" from being forced to move together; the second should track what + # penguin runs, which is never a four-year-old qemu. + nixpkgs-qemu.url = "github:NixOS/nixpkgs/nixos-25.05"; + + # Source only -- igloo_driver has no flake of its own yet. This is here to + # ACCEPTANCE-TEST the kernel `dev` output (see nix/driver.nix): a build tree + # that cannot build the one module we care about is broken, and this repo is + # where that should be caught. It is not a claim about which repo should own + # the driver build long-term. + igloo_driver = { + url = "github:rehosting/igloo_driver"; + flake = false; + }; + }; + + outputs = + { self, nixpkgs, nixpkgs-qemu, kernelsmith, igloo_driver }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + qemuPkgs = nixpkgs-qemu.legacyPackages.${system}; + inherit (pkgs) lib; + + # Upstream bases. These are REAL RELEASE TAGS -- the point of the patchset + # migration. Previously linux/6.13 was pinned to a tree based on v6.13~5 + # with three upstream commits cherry-picked forward, which no one would + # choose deliberately; those three are now simply present in the tarball. + bases = { + "4.10" = { + tag = "4.10"; + hash = "sha256-PJXZ8Em9CF5cNG0sd/BjuEJfGRRg/NOun+fpTgR33Es="; + }; + "6.13" = { + tag = "6.13"; + hash = "sha256-553Mbrhmlca6v7B8KGGRK2NdUHXGzRzQVn0eoVX4DW4="; + }; + }; + + matrix = import ./nix/matrix.nix; + configLib = import ./nix/config.nix { inherit pkgs; }; + sourceLib = import ./nix/source.nix { inherit pkgs; }; + mkKernel = import ./nix/kernel.nix { inherit pkgs kernelsmith; }; + + # Arches kernelsmith cannot resolve a toolchain for yet. Listed explicitly + # rather than silently dropped, so `nix build .#all` never quietly ships a + # smaller matrix than build.sh does. + # + # Now empty: riscv64 landed as a Bootlin k6 pin, and loongarch64 as a + # kernel-only gcc 13.3 (no libc toolchain exists for it from any source -- + # see kernelsmith's matrix.k6LoongarchKernel). Kept as a mechanism rather + # than deleted, so a future arch gap is declared here instead of silently + # shrinking `nix build .#all` below what build.sh covers. + kernelsmithMissing = [ ]; + + buildable = version: builtins.filter (t: !(builtins.elem t kernelsmithMissing)) matrix.${version}; + + kernelSrcFor = version: sourceLib.kernelSource { + patchesRoot = ./patches; + inherit version; + base = bases.${version}; + }; + + # Memoise per version: one patched source tree feeds every target. + sources = lib.genAttrs (builtins.attrNames matrix) kernelSrcFor; + + cellFor = version: target: mkKernel { + inherit version target; + src = sources.${version}; + config = configLib.rawConfig { + configsSrc = ./configs; + inherit version target; + }; + }; + + cells = lib.listToAttrs (lib.concatMap + (version: map + (target: lib.nameValuePair "kernel-${version}-${target}" (cellFor version target)) + (buildable version)) + (builtins.attrNames matrix)); + + # ---- the release seam --------------------------------------------- + # Keep emitting the two tarballs penguin consumes today, so switching + # linux_builder to nix does not require touching penguin at all. Moving + # penguin to a flake input is a separate, later change. + analysisLib = import ./nix/analysis.nix { inherit pkgs; }; + releaseLib = import ./nix/release.nix { inherit pkgs; }; + mkPerf = import ./nix/perf.nix { inherit pkgs kernelsmith; }; + mkDriver = import ./nix/driver.nix { inherit pkgs kernelsmith; }; + bootLib = import ./nix/boot.nix { inherit pkgs qemuPkgs; }; + lintLib = import ./nix/lint.nix { inherit pkgs; }; + cfgTools = import ./nix/config-tools.nix { inherit pkgs; }; + + # Per-cell record carrying everything the assembly needs. + cellRecords = version: map + (target: + let kernel = cells."kernel-${version}-${target}"; in { + inherit version target kernel; + # Carried so nix/lint.nix can re-run savedefconfig over exactly the + # tree and fragment this cell was built from. + src = sources.${version}; + config = configLib.rawConfig { + configsSrc = ./configs; + inherit version target; + }; + osi = analysisLib.osiConfig { inherit kernel version target; }; + cosi = analysisLib.cosiJson { inherit kernel version target; }; + perf = mkPerf { + inherit version target; + src = sources.${version}; + inherit (kernel) arch; + }; + driver = mkDriver { + inherit kernel version target; + src = igloo_driver; + }; + }) + (buildable version); + + allRecords = lib.concatMap cellRecords (builtins.attrNames matrix); + + # Per-cell analysis/perf outputs, individually addressable. Without these + # a broken perf can only be reached through the whole release tarball, + # which rebuilds everything to show you one compiler error. + perCellOutputs = lib.listToAttrs (lib.concatMap + (r: [ + (lib.nameValuePair "perf-${r.version}-${r.target}" r.perf) + (lib.nameValuePair "driver-${r.version}-${r.target}" r.driver) + (lib.nameValuePair "osi-${r.version}-${r.target}" r.osi) + (lib.nameValuePair "cosi-${r.version}-${r.target}" r.cosi) + # The one check that asks whether the kernel RUNS. Individually + # addressable so a dead cell can be reproduced in one command: + # nix build .#packages.x86_64-linux."boot-4.10-x86_64" + (lib.nameValuePair "boot-${r.version}-${r.target}" + (bootLib.forCell { inherit (r) kernel version target; })) + # Replaces `./build.sh --config-only`. Advisory, not a gate -- see + # nix/lint.nix for why asserting on it would be wrong. + (lib.nameValuePair "config-lint-${r.version}-${r.target}" + (lintLib.forCell { inherit (r) kernel config src version target; })) + ]) + allRecords); + + in + { + packages.${system} = cells // perCellOutputs // { + default = cells."kernel-6.13-armel"; + + # Everything buildable today, in one derivation, for CI. + all = pkgs.linkFarm "igloo-kernels-all" + (lib.mapAttrsToList (n: v: { name = n; path = v; }) cells); + + # The same payload as kernels-latest, but as a directory. This is the + # seam for Nix consumers: penguin stages `/...` straight into + # /igloo_static/kernels/, so handing it the tree avoids packing an + # archive purely for the consumer to unpack again. + kernels = releaseLib.kernelsTree { + versions = map + (version: { + inherit version; + dir = releaseLib.kernelsDir { inherit version; cells = cellRecords version; }; + }) + (builtins.attrNames matrix); + }; + + # Drop-in replacements for the Docker build's release artifacts. + kernels-latest = releaseLib.kernelsTarball { + versions = map + (version: { + inherit version; + dir = releaseLib.kernelsDir { inherit version; cells = cellRecords version; }; + }) + (builtins.attrNames matrix); + }; + kernel-devel-all = releaseLib.develTarball { cells = allRecords; }; + + # Catches the failure class where a cell builds fine and produces an + # artifact whose ELF class or endianness disagrees with its target name. + # See nix/shape.nix -- two such bugs shipped undetected on this branch. + shape-check = (import ./nix/shape.nix { inherit pkgs; }) { cells = allRecords; }; + + # Catches the strictly worse failure class shape-check cannot see: an + # artifact of exactly the right shape and size that does not run at all. + # nixdev_0.1.0's 4.10/x86_64 is one -- see nix/boot.nix. + boot-check = bootLib.all { cells = allRecords; }; + + # Every cell's savedefconfig lint in one build, for a config sweep. + # The nix replacement for `./build.sh --config-only` across all targets. + config-lint = lintLib.all { cells = allRecords; }; + + # THE config gate: does every cell END UP with what IGLOO needs? + # Reads the post-olddefconfig .config, not the fragment -- see + # nix/config-tools.nix for why that distinction is the whole point. + config-required = cfgTools.requiredCheck { cells = allRecords; }; + + # Duplicated assignments, and options that did not survive + # olddefconfig. Advisory. + config-redundant = cfgTools.redundancyReport { + cells = allRecords; + configsSrc = ./configs; + }; + + # `nix run .#config-explain -- 6.13 x86_64 CONFIG_IGLOO` + config-explain = cfgTools.explainScript; + + # The analysis tools, pinned. Exposed so their provenance is inspectable + # and so igloo_driver can reuse dwarf2json for its own ISF (it runs the + # same fork over igloo.ko) instead of re-deriving the pin. + inherit (analysisLib) dwarf2json extractKernelinfo; + }; + + # The patched source trees, so `nix build .#sources.x86_64-linux."6.13"` + # gives you exactly what the kernel builds from -- useful for inspecting + # what the series produces without a full kernel build. + inherit sources; + + # `nix run .#config-explain -- CONFIG_X`. An app rather + # than only a package so it is one command from a clean checkout; it reads + # configs/ and builds no kernel, so it stays instant. + apps.${system}.config-explain = { + type = "app"; + program = "${cfgTools.explainScript}/bin/config-explain"; + }; + + # Cells we cannot build until kernelsmith gains these arches. + missingArches = kernelsmithMissing; + + devShells.${system}.default = pkgs.mkShell { + packages = with pkgs; [ gnumake bc bison flex openssl elfutils git ]; + shellHook = '' + echo "linux_builder (nix-patchset)" + # NB: cell names contain a '.', which nix would parse as an attrpath + # separator -- quote the attribute or the build fails to resolve. + echo ' nix build .#packages.x86_64-linux."kernel-6.13-armel" one cell' + echo " nix build .#all every buildable cell" + echo " nix build .#boot-check every kernel actually boots" + echo " nix build .#config-required configs satisfy the IGLOO contract (gate)" + echo ' nix run .#config-explain -- 6.13 x86_64 CONFIG_IGLOO where does an option come from' + echo " nix build .#config-redundant duplicate / dead config lines" + echo " nix build .#config-lint savedefconfig lint (was build.sh --config-only)" + echo " ./scripts/verify-series.sh ... prove a series matches its fork branch" + ''; + }; + }; +} diff --git a/linux/4.10 b/linux/4.10 deleted file mode 160000 index 6899687..0000000 --- a/linux/4.10 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 68996875436ed6bd835047465f364916d468d394 diff --git a/linux/6.13 b/linux/6.13 deleted file mode 160000 index 8f25e98..0000000 --- a/linux/6.13 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8f25e989e12e2fca57aec18413ad157efb452ee4 diff --git a/nix/analysis.nix b/nix/analysis.nix new file mode 100644 index 0000000..c75f4d9 --- /dev/null +++ b/nix/analysis.nix @@ -0,0 +1,108 @@ +# The two analysis artifacts shipped alongside each kernel: +# +# osi..config -- PANDA osi_linux kernel profile (struct offsets), +# extracted by running gdb over the DEBUGGABLE vmlinux +# cosi..json.xz -- volatility-style symbol table from the same vmlinux +# +# Both read the UNSTRIPPED build-tree vmlinux. That is why kernel.nix exposes a +# separate `vmlinux` output and does not strip in place: _in_container_build.sh +# gets away with it only by ordering (extract first, strip the shipped copy +# afterwards), which a derivation cannot reproduce once the kernel is realised. +# +# PROVENANCE NOTE -- both tools are fetched unpinned today. +# +# * extract_kernelinfo: the Dockerfile wgets it from `refs/heads/main` of +# panda-re/panda-ng at image-build time, from a repo the stack is actively +# retiring (CLAUDE.md: panda-ng is on the way out, new work goes to qemu/). +# * dwarf2json: embedded-toolchains' Dockerfile does `git clone --depth 1` of +# the DEFAULT BRANCH of rehosting/dwarf2json -- a FORK, not upstream +# volatilityfoundation. nixpkgs ships the upstream tool under that name; using +# it would silently produce different ISFs, and penguin consumes them at +# runtime (pyplugins/apis/kffi.py reads cosi..json.xz). So build the +# fork from a pinned rev. +# +# Every osi.config and cosi ISF shipped to date was produced by "whatever the +# default branch said that day". Both are pinned to commits here. +{ pkgs }: + +let + inherit (pkgs) lib; + + # rehosting/dwarf2json @ main. Sole dependency is spf13/pflag, so the vendor + # tree is tiny -- but it must still be a fixed-output vendor derivation. + dwarf2json = pkgs.buildGoModule { + pname = "dwarf2json"; + version = "unstable-2026-rehosting-fork"; + src = pkgs.fetchFromGitHub { + owner = "rehosting"; + repo = "dwarf2json"; + rev = "45f9343560b7ece6be23415695fe4d0c2678759d"; + hash = "sha256-cFIXDmVv58DBtj89Wb77ZjtK6vz5LTF/wKPpEd88t9M="; + }; + vendorHash = "sha256-3PnXB8AfZtgmYEPJuh0fwvG38dtngoS/lxyx3H+rvFs="; + meta.description = "rehosting fork of dwarf2json (produces the COSI ISFs penguin loads)"; + }; + + # panda-re/panda-ng, plugins/osi_linux/utils/kernelinfo_gdb. + # Last touched 2025-04-14 ("try pcpu_hot"); pinned rather than tracking main. + kernelinfoRev = "1764d2efe73712944996647b582862522f36efc9"; + kernelinfoUrl = f: + "https://raw.githubusercontent.com/panda-re/panda-ng/${kernelinfoRev}/plugins/osi_linux/utils/kernelinfo_gdb/${f}"; + + extractKernelinfo = pkgs.runCommand "extract-kernelinfo-${lib.substring 0 8 kernelinfoRev}" + { + py = pkgs.fetchurl { + url = kernelinfoUrl "extract_kernelinfo.py"; + hash = "sha256-CO9UYvk+WwZHo2zGVNYwHgy6TUR9D7Ysn/FYNH/yGfA="; + }; + sh = pkgs.fetchurl { + url = kernelinfoUrl "run.sh"; + hash = "sha256-Df4GI32UmioUc5/830XglQ8Er50Kj4md1y6uyOAt2ZU="; + }; + } '' + mkdir -p $out + cp $py $out/extract_kernelinfo.py + cp $sh $out/run.sh + chmod +x $out/run.sh + # run.sh is `#!/bin/bash`, which does not exist in the sandbox -- the same + # class of breakage as 4.10's `/bin/pwd` in kernel.nix. Unpatched it fails + # with the deeply unhelpful "cannot execute: required file not found". + patchShebangs $out/run.sh + ''; + +in +rec { + inherit extractKernelinfo dwarf2json; + + # gdb reads a foreign-arch vmlinux fine: nixpkgs builds it --enable-targets=all, + # so no per-arch gdb is needed (the Docker build relies on Ubuntu's multiarch + # gdb for the same reason, just without saying so). + osiConfig = { kernel, version, target }: + pkgs.runCommand "igloo-osi-${version}-${target}" + { + nativeBuildInputs = [ pkgs.gdb pkgs.bash ]; + meta.description = "PANDA osi_linux profile for ${version}/${target}"; + } '' + vmlinux=${kernel.vmlinux}/vmlinux.${target} + test -f "$vmlinux" || { echo "no unstripped vmlinux at $vmlinux"; exit 1; } + + # Faithful to _in_container_build.sh: the [target] section header is + # written FIRST, then the extractor's output is appended to it. + echo "[${target}]" > $out + ${extractKernelinfo}/run.sh "$vmlinux" profile.out + test -s profile.out || { echo "extract_kernelinfo produced nothing"; exit 1; } + cat profile.out >> $out + ''; + + cosiJson = { kernel, version, target }: + pkgs.runCommand "igloo-cosi-${version}-${target}" + { + # NOT pkgs.dwarf2json -- that is upstream volatility's, not our fork. + nativeBuildInputs = [ dwarf2json pkgs.xz ]; + meta.description = "COSI symbol table for ${version}/${target}"; + } '' + vmlinux=${kernel.vmlinux}/vmlinux.${target} + test -f "$vmlinux" || { echo "no unstripped vmlinux at $vmlinux"; exit 1; } + dwarf2json linux --elf "$vmlinux" | xz -c > $out + ''; +} diff --git a/nix/boot.nix b/nix/boot.nix new file mode 100644 index 0000000..21df0cd --- /dev/null +++ b/nix/boot.nix @@ -0,0 +1,172 @@ +# Boot every shipped kernel under qemu and assert it actually starts. +# +# THIS IS THE CHECK THAT WAS MISSING. nixdev_0.1.0 shipped a 4.10/x86_64 +# bzImage that compiles, links, packages, has the correct ELF class, byte order +# and machine, is the same 8.1 MB as the Docker build's -- and prints not one +# line before dying. Every existing gate passed it: +# +# * the build succeeded +# * shape-check passed (a bzImage's shape says nothing about whether it runs) +# * the series gate passed (it only proves the patches APPLY) +# * kernelsmith's own boot.nix passed -- but its k4 cell boots 5.10.229, and +# the k4 band spans 4.x AND 5.x, so 4.10 had never been booted by anything +# +# It was found by a downstream consumer's integration tests (rehosting/penguin#932), +# which is three repos and one release too late. +# +# The machine/console table is transcribed from penguin's src/penguin/arch_registry.py +# -- deliberately, so this boots each kernel on the machine penguin will actually +# run it on. A kernel that boots under some other qemu model is not the claim +# anyone downstream needs. +# `qemuPkgs` is a SEPARATE nixpkgs from `pkgs` -- see flake.nix's nixpkgs-qemu +# input. The kernel build's pin is 24.05, whose qemu is 8.2.7 and ships no +# loongarch64 EFI firmware, so that target could not be booted at all. +{ pkgs, qemuPkgs }: + +let + inherit (pkgs) lib; + qemu = qemuPkgs.qemu; + + # target -> how penguin boots it. `null` means "no qemu machine exists", + # which is DECLARED here rather than silently skipped. + machines = { + armel = { system = "arm"; machine = "virt"; console = "ttyAMA0"; }; + arm64 = { system = "aarch64"; machine = "virt"; cpu = "cortex-a57"; console = "ttyAMA0"; }; + mipsel = { system = "mipsel"; machine = "malta"; console = "ttyS0"; }; + mipseb = { system = "mips"; machine = "malta"; console = "ttyS0"; }; + mips64el = { system = "mips64el"; machine = "malta"; cpu = "MIPS64R2-generic"; console = "ttyS0"; }; + mips64eb = { system = "mips64"; machine = "malta"; cpu = "MIPS64R2-generic"; console = "ttyS0"; }; + powerpc64 = { system = "ppc64"; machine = "pseries"; cpu = "power9"; console = "hvc0"; }; + powerpc64le = { system = "ppc64"; machine = "pseries"; cpu = "power9"; console = "hvc0"; }; + riscv64 = { system = "riscv64"; machine = "virt"; console = "ttyS0"; }; + # loongarch64 needs two things no other target here does, and penguin + # supplies both -- see penguin_run.py's `-bios edk2-loongarch64-code.fd`. + # `mem`: qemu's virt machine refuses to start below 1G + # ("ram_size must be greater than 1G") + # `bios`: its kernel_fmt is vmlinuz.efi, a PE image, so the built-in + # loader rejects it ("The image is not ELF"). EFI firmware is + # what boots it. Booting the ELF vmlinux instead would pass this + # test while testing an image penguin never runs. + loongarch64 = { + system = "loongarch64"; machine = "virt"; cpu = "la464"; console = "ttyS0"; + mem = 2048; bios = "edk2-loongarch64-code.fd"; + }; + x86_64 = { system = "x86_64"; machine = "pc"; console = "ttyS0"; }; + + # 32-bit powerpc: arch_registry.py records qemu_machine=None -- "no QEMU + # machine was ever configured for 32-bit ppc". Nothing to boot it on, so it + # is unbootable-by-declaration rather than an oversight. If a machine is + # ever wired up in penguin, add it here too. + powerpc = null; + }; + + # The kernel image each target ships. Genuinely globbed -- an earlier version + # of this claimed to glob but actually hard-coded four names + # (bzImage/zImage/Image/vmlinux), and 6.13/loongarch64 ships + # `vmlinuz.efi.loongarch64`, so it failed as "no bootable image" while the + # kernel itself was fine. Every artifact is named `.`, so + # match on that suffix and rank the hits: prefer whatever the arch's boot + # wrapper produces, fall back to raw vmlinux. + bootTest = { kernel, version, target, spec }: + pkgs.runCommand "igloo-boot-${version}-${target}" + { + nativeBuildInputs = [ qemu ]; + meta.description = "boot smoke test for ${version}/${target}"; + } '' + # Rank every *.${target} artifact; first match wins. vmlinux is last + # deliberately: where an arch ships both, the wrapped image is what + # penguin boots, so that is what this must test. + img="" + for pat in bzImage zImage vmlinuz.efi vmlinuz uImage Image vmlinux; do + cand="${kernel}/$pat.${target}" + [ -f "$cand" ] && { img="$cand"; break; } + done + # Nothing ranked matched -- take any *.${target} regular file rather than + # failing, so a new arch's novel image name is a warning, not an outage. + if [ -z "$img" ]; then + for cand in ${kernel}/*.${target}; do + [ -f "$cand" ] || continue + case "$(basename "$cand")" in Module.symvers*|config*|*.map) continue;; esac + echo "warning: unranked image name $(basename "$cand") -- add it to the rank list" >&2 + img="$cand"; break + done + fi + if [ -z "$img" ]; then + echo "no bootable image for ${version}/${target} in ${kernel}" >&2 + ls ${kernel} >&2 + exit 1 + fi + echo "booting $(basename $img) on qemu-system-${spec.system} -M ${spec.machine}" + + # No rootfs is supplied on purpose. A kernel that starts and then cannot + # mount root is a SUCCESS for this test -- it proves early boot, console + # init and the whole pre-userspace path. Supplying a rootfs would test + # penguin's job, not this one. + # + # `timeout` rather than qemu's own exit: a kernel that hangs must fail + # here, not run until the CI job is killed. + timeout 120 qemu-system-${spec.system} \ + -M ${spec.machine} \ + ${lib.optionalString (spec ? cpu) "-cpu ${spec.cpu}"} \ + ${lib.optionalString (spec ? bios) "-bios ${qemu}/share/qemu/${spec.bios}"} \ + -m ${toString (spec.mem or 256)} -nographic -no-reboot \ + -kernel "$img" \ + -append "console=${spec.console} panic=1" \ + < /dev/null > boot.log 2>&1 || true + + echo "--- captured $(wc -c < boot.log) bytes ---" + cat boot.log + + # Two assertions, in increasing strength. + # + # 1. The kernel produced kernel log output at all. This is what + # nixdev_0.1.0's 4.10/x86_64 failed: it decompressed, printed + # "Booting the kernel.", and went silent forever. + # + # Matching the "Linux version" banner ALONE is too strict: on + # loongarch64 the EFI stub hands over after the console is set up, so + # the earliest printks -- the banner among them -- never reach the + # serial log, and a kernel that booted all the way to a root-fs panic + # was reported as never having started. A timestamped printk is the + # portable evidence of "the kernel is running"; the dead x86_64 image + # emitted none. + if ! grep -Eq "Linux version|^\[[ ]*[0-9]+\.[0-9]+\]" boot.log; then + echo "FAIL ${version}/${target}: no kernel output at all -- it never started" >&2 + exit 1 + fi + + # 2. It got as far as looking for a root filesystem. Without this a kernel + # that prints its banner and then dies in early init would still pass, + # which is most of the failure surface this test exists for. + if ! grep -Eq "VFS: Cannot open root|VFS: Unable to mount root|Kernel panic - not syncing|No filesystem could mount root|Attempted to kill init|Requested init" boot.log; then + echo "FAIL ${version}/${target}: banner printed but never reached the root-fs stage" >&2 + exit 1 + fi + + echo "ok ${version}/${target}" > $out + ''; + +in +rec { + inherit machines; + + # null spec -> a derivation that records WHY it is not boot-tested. Not a + # silent omission: `nix build .#boot-check` still names it. + forCell = { kernel, version, target }: + let spec = machines.${target} or (throw + "boot.nix: target ${target} has no entry; add it (or an explicit null)"); + in + if spec == null then + pkgs.runCommand "igloo-boot-${version}-${target}-skipped" { } '' + echo "SKIP ${version}/${target}: no qemu machine exists for this target" | tee $out + '' + else bootTest { inherit kernel version target spec; }; + + # One derivation that boots the whole matrix, for CI. + all = { cells }: + pkgs.linkFarm "igloo-boot-check" + (map (c: { + name = "${c.version}-${c.target}"; + path = forCell { inherit (c) kernel version target; }; + }) cells); +} diff --git a/nix/config-tools.nix b/nix/config-tools.nix new file mode 100644 index 0000000..25124e6 --- /dev/null +++ b/nix/config-tools.nix @@ -0,0 +1,349 @@ +# Tools for working on configs/. +# +# configs// is a cpp fragment that `#include`s shared pieces, +# so the file you edit is almost never the file an option comes from -- and the +# config the kernel is BUILT with is a third thing again, because `olddefconfig` +# runs afterwards and will silently drop any option whose dependencies are not +# met. Three questions follow from that, and each gets a tool here: +# +# config-required Did every cell END UP with the options IGLOO needs? +# config-explain Where does CONFIG_X for this cell come from? +# config-redundant Which lines I wrote are doing nothing? +# +# The first is a gate. The other two are answers to questions, not assertions. +{ pkgs }: + +let + inherit (pkgs) lib; + + # --------------------------------------------------------------------- + # The contract: what a kernel must have for IGLOO to work on it. + # + # Every entry is here because something downstream breaks without it, and + # each names what. This is deliberately NOT "the options we happen to set" -- + # it is the subset whose absence is a silent failure, i.e. a kernel that + # builds, boots, and then does not do its job. That failure class is the + # reason this file exists; see nix/boot.nix for the sibling case. + required = { + CONFIG_IGLOO = "the IGLOO patch series itself -- without it every igloo_* hook compiles out and the kernel is stock"; + CONFIG_MODULES = "igloo.ko is a module; nothing to load it into"; + CONFIG_MODULE_UNLOAD = "penguin unloads and reloads the driver between runs"; + CONFIG_MODVERSIONS = "the driver/kernel ABI CRCs. Without it a mismatched module loads SILENTLY instead of being rejected -- strictly worse than the mismatch"; + CONFIG_KALLSYMS_ALL = "OSI symbol resolution reads kallsyms"; + CONFIG_KPROBES = "penguin's kprobe-based instrumentation"; + CONFIG_DEBUG_INFO = "dwarf2json builds the ISF from DWARF; no debug info, no ISF"; + }; + + # --------------------------------------------------------------------- + # Shared include-resolver. cpp semantics: `#include "x.inc"` splices x.inc in + # place, and a later assignment of the same symbol overrides an earlier one. + # Both tools below need this, so it lives once. + resolverPy = '' + import os + import re + import sys + import collections + + INCLUDE = re.compile(r'\s*#include\s+"([^"]+)"') + ASSIGN = re.compile(r'\s*(CONFIG_[A-Za-z0-9_]+)\s*=\s*(.*?)\s*$') + UNSET = re.compile(r'\s*#\s*(CONFIG_[A-Za-z0-9_]+)\s+is not set\s*$') + + + def walk(path, chain=None, out=None, seen=None): + """Yield (option, value, file, line, include-chain) in cpp order.""" + chain = (chain or []) + [os.path.basename(path)] + out = out if out is not None else [] + seen = seen if seen is not None else set() + real = os.path.realpath(path) + if real in seen: # cpp would loop forever; we just stop + return out + seen.add(real) + d = os.path.dirname(path) + for n, line in enumerate(open(path, errors='replace'), 1): + m = INCLUDE.match(line) + if m: + inc = os.path.join(d, m.group(1)) + if os.path.exists(inc): + walk(inc, chain, out, seen) + else: + out.append(('!MISSING', m.group(1), path, n, list(chain))) + continue + m = ASSIGN.match(line) + if m: + out.append((m.group(1), m.group(2), path, n, list(chain))) + continue + m = UNSET.match(line) + if m: + out.append((m.group(1), 'n', path, n, list(chain))) + return out + + + def final(entries): + """Last assignment wins, exactly as cpp+Kconfig would see it.""" + v = {} + for opt, val, f, n, chain in entries: + if opt == '!MISSING': + continue + v[opt] = (val, f, n, chain) + return v + ''; + + # Parse a real .config (post-olddefconfig) -- the ground truth. + configReaderPy = '' + + + def read_config(path): + vals = {} + for line in open(path, errors='replace'): + line = line.rstrip('\n') + m = re.match(r'(CONFIG_[A-Za-z0-9_]+)=(.*)$', line) + if m: + vals[m.group(1)] = m.group(2); continue + m = re.match(r'# (CONFIG_[A-Za-z0-9_]+) is not set$', line) + if m: + vals[m.group(1)] = 'n' + return vals + ''; + +in +rec { + inherit required; + + # --------------------------------------------------------------------- + # 1. THE GATE. + # + # Reads each cell's POST-olddefconfig .config out of the kernel's dev output, + # not the fragment. That distinction is the whole point: `olddefconfig` + # silently drops an option whose dependencies are unmet, so a fragment that + # says CONFIG_MODVERSIONS=y proves nothing about the kernel that shipped. + # + # Reports EVERY violation across EVERY cell before failing. A gate that stops + # at the first problem turns one fix-and-rerun cycle into N. + requiredCheck = { cells }: + let + configs = lib.concatMapStringsSep " " (c: "${c.version}:${c.target}:${c.kernel.dev}/.config") cells; + in + pkgs.runCommand "igloo-config-required" + { + nativeBuildInputs = [ pkgs.python3 ]; + meta.description = "assert every cell ends up with the options IGLOO needs"; + } '' + cat > check.py <<'EOF' + import re, sys + ${configReaderPy} + + REQUIRED = { + ${lib.concatStringsSep "\n" (lib.mapAttrsToList + (opt: why: " ${builtins.toJSON opt}: ${builtins.toJSON why},") required)} + } + + cells = [c.split(':', 2) for c in sys.argv[1:]] + failures = [] + for version, target, path in cells: + vals = read_config(path) + for opt, why in sorted(REQUIRED.items()): + got = vals.get(opt) + if got in ('y', 'm'): + continue + failures.append((version, target, opt, got, why)) + + print("checked %d cells against %d required options" % (len(cells), len(REQUIRED))) + if not failures: + print("all cells satisfy the IGLOO config contract") + sys.exit(0) + + print("") + print("%d violation(s):" % len(failures)) + for version, target, opt, got, why in failures: + state = "unset" if got is None else ("=" + got) + print(" %s/%s %s %s" % (version, target, opt, state)) + print(" needed for: %s" % why) + print("") + print("NOTE: this reads the POST-olddefconfig .config, so an option can be") + print("set in configs/ and still fail here -- olddefconfig drops options") + print("whose dependencies are unmet. Use `nix run .#config-explain` to see") + print("where the fragment sets it, then check what its dependencies need.") + sys.exit(1) + EOF + + # NOT `python3 ... | tee $out`. A pipeline's status is its LAST command, + # so a failing check would be masked by a succeeding tee unless pipefail + # happens to be set. stdenv does set it -- but a gate that silently passes + # when a shell option changes is the exact failure class this file exists + # to catch, so don't depend on it. + python3 check.py ${configs} > report.txt; status=$? + cat report.txt + cp report.txt $out + exit $status + ''; + + # --------------------------------------------------------------------- + # 2. PROVENANCE. `nix run .#config-explain -- 6.13 x86_64 CONFIG_IGLOO` + # + # Deliberately does NOT build a kernel: this answers "where does this come + # from", which is a question about configs/, and making it cost a cross-build + # would mean nobody runs it. It reports what the fragments say and is explicit + # that olddefconfig gets the last word -- rather than quietly implying the + # fragment value is what shipped. + explainScript = pkgs.writers.writePython3Bin "config-explain" { flakeIgnore = [ "E501" "F401" ]; } '' + ${resolverPy} + + def main(): + if len(sys.argv) not in (4, 5): + print("usage: config-explain [configs-dir]", file=sys.stderr) + print(" eg: config-explain 6.13 x86_64 CONFIG_IGLOO", file=sys.stderr) + return 2 + version, target, opt = sys.argv[1], sys.argv[2], sys.argv[3] + root = sys.argv[4] if len(sys.argv) == 5 else "configs" + if not opt.startswith("CONFIG_"): + opt = "CONFIG_" + opt + + path = os.path.join(root, version, target) + if not os.path.exists(path): + print("no config for %s/%s at %s" % (version, target, path), file=sys.stderr) + return 1 + + entries = walk(path) + + missing = [e for e in entries if e[0] == '!MISSING'] + for _, inc, f, n, _ in missing: + print("warning: %s:%d includes %s, which does not exist" % (f, n, inc), file=sys.stderr) + + hits = [e for e in entries if e[0] == opt] + if not hits: + print("%s is not set anywhere in %s/%s's fragment tree." % (opt, version, target)) + print("") + print("Include chain searched:") + seen = [] + for _, _, _, _, chain in entries: + key = " -> ".join(chain) + if key not in seen: + seen.append(key) + for s in seen: + print(" %s" % s) + print("") + print("It may still be enabled in the built kernel: olddefconfig turns on") + print("options that other options select. Check the shipped .config.") + return 0 + + print("%s for %s/%s" % (opt, version, target)) + print("") + for i, (_, val, f, n, chain) in enumerate(hits): + last = (i == len(hits) - 1) + print(" %s %s=%s" % ("*" if last else " ", opt, val)) + print(" %s:%d" % (f, n)) + print(" via %s" % " -> ".join(chain)) + if len(hits) > 1: + print("") + print(" %d assignments; the one marked * wins (cpp: last wins)." % len(hits)) + print(" The earlier ones are dead weight -- see `nix build .#config-redundant`.") + print("") + print("NOTE: this is what the FRAGMENTS say. olddefconfig runs afterwards and") + print("has the last word -- it drops options whose dependencies are unmet.") + print("`nix build .#config-required` checks the shipped .config instead.") + return 0 + + + sys.exit(main()) + ''; + + # --------------------------------------------------------------------- + # 3. REDUNDANCY. Two genuinely different kinds, reported together: + # + # (a) STATIC -- an option assigned more than once in one cell's fragment + # tree. Purely a configs/ bug, needs no kernel, always actionable. + # 6.13/all-common.inc sets CONFIG_MODULES=y twice, 3 lines apart. + # + # (b) DEFAULTED -- an option whose value already matches what the arch + # gives you, so writing it changes nothing. Needs savedefconfig, hence + # the built kernel. + # + # (a) is the one worth acting on: a duplicate is always a mistake, whereas a + # defaulted option is often deliberately explicit. So they are never merged + # into one number. + redundancyReport = { cells, configsSrc }: + let + args = lib.concatMapStringsSep " " + (c: "${c.version}:${c.target}:${c.kernel.dev}/.config") cells; + in + pkgs.runCommand "igloo-config-redundant" + { + nativeBuildInputs = [ pkgs.python3 ]; + meta.description = "which config lines are duplicated or already the default"; + } '' + cp -r ${configsSrc} configs && chmod -R u+w configs + + cat > report.py <<'EOF' + import re, sys, os, collections + ${resolverPy} + ${configReaderPy} + + cells = [c.split(':', 2) for c in sys.argv[1:]] + total_dupes = 0 + + print("=" * 70) + print("DUPLICATE ASSIGNMENTS -- an option set more than once in one cell") + print("=" * 70) + print("These are always bugs: only the last one has any effect.") + print("") + for version, target, cfgpath in cells: + entries = walk(os.path.join("configs", version, target)) + byopt = collections.OrderedDict() + for opt, val, f, n, chain in entries: + if opt == '!MISSING': + continue + byopt.setdefault(opt, []).append((val, f, n)) + dupes = {o: v for o, v in byopt.items() if len(v) > 1} + if not dupes: + continue + print("%s/%s: %d option(s) assigned more than once" % (version, target, len(dupes))) + for opt, occurrences in sorted(dupes.items()): + vals = {v for v, _, _ in occurrences} + kind = "same value" if len(vals) == 1 else "CONFLICTING values" + print(" %s (%d times, %s)" % (opt, len(occurrences), kind)) + for val, f, n in occurrences: + print(" %s=%s at %s:%d" % (opt, val, f, n)) + total_dupes += 1 + print("") + if total_dupes == 0: + print("none found.") + print("") + + print("=" * 70) + print("DEFAULTED OPTIONS -- written, but already the arch default") + print("=" * 70) + print("Not necessarily bugs: being explicit about an important option is") + print("a legitimate choice. Counts only, per cell, so this stays readable.") + print("") + for version, target, cfgpath in cells: + entries = walk(os.path.join("configs", version, target)) + written = final(entries) + shipped = read_config(cfgpath) + # An option we wrote whose shipped value matches, but which we cannot + # tell apart from "we asked and got it" without savedefconfig. What we + # CAN say cheaply and truthfully: which written options did not survive. + dropped = [] + for opt, (val, f, n, chain) in sorted(written.items()): + got = shipped.get(opt) + if got is None: + dropped.append((opt, val, f, n, "not present in shipped .config")) + elif got != val: + dropped.append((opt, val, f, n, "shipped as %s" % got)) + print("%s/%s: wrote %d options; %d did not survive olddefconfig" + % (version, target, len(written), len(dropped))) + for opt, val, f, n, why in dropped: + print(" %s=%s (%s) %s:%d" % (opt, val, why, f, n)) + print("") + + print("For the full savedefconfig diff per cell, build .#config-lint.") + if total_dupes: + print("") + print("%d duplicated option(s) found -- these are worth fixing." % total_dupes) + EOF + + python3 report.py ${args} > report.txt; status=$? + cat report.txt + cp report.txt $out + exit $status + ''; +} diff --git a/nix/config.nix b/nix/config.nix new file mode 100644 index 0000000..ee1a2d6 --- /dev/null +++ b/nix/config.nix @@ -0,0 +1,30 @@ +# .config assembly -- deliberately identical to what _in_container_build.sh does: +# +# cpp -P -undef configs// -> .config +# make olddefconfig +# +# configs/ is UNCHANGED by the nix migration. The `#include "arm-common.inc"` +# fragments resolve relative to the including file, so pointing cpp at the +# config inside the copied configs tree is all that's needed. +# +# This is why the linuxManualConfig-vs-nixpkgs-config-machinery design fork +# never arises: we keep our own assembly, and nix only has to run it. +{ pkgs }: + +{ + # Just the cpp step. The `olddefconfig` half needs the kernel tree + toolchain, + # so it happens inside the kernel derivation (see kernel.nix). + rawConfig = + { configsSrc, version, target }: + pkgs.runCommand "igloo-config-${version}-${target}" + { + nativeBuildInputs = [ pkgs.stdenv.cc ]; + } + '' + if [ ! -f ${configsSrc}/${version}/${target} ]; then + echo "no config for ${target} at version ${version}" >&2 + exit 1 + fi + cpp -P -undef ${configsSrc}/${version}/${target} -o $out + ''; +} diff --git a/nix/driver.nix b/nix/driver.nix new file mode 100644 index 0000000..0afa559 --- /dev/null +++ b/nix/driver.nix @@ -0,0 +1,41 @@ +# igloo.ko per cell, built against this flake's kernel derivation. +# +# This exists here rather than in igloo_driver's own repo for one reason: it is +# the acceptance test for kernelsmith's `buildModule` and for the `dev` (build +# tree) output. If a module cannot be built from a linux_builder cell without +# unpacking a tarball, the devel output is wrong, and it is better to find that +# out in this repo's CI than in igloo_driver's. +# +# The CRC property is the point. `_in_container_build.sh` extracts +# kernel-devel-all.tar.gz to a scratch directory and builds against whatever is +# there, so nothing structurally prevents building a module against a different +# kernel than the one it will be inserted into -- the failure mode is a module +# that loads and then misbehaves, or refuses to load with a version magic +# mismatch, depending on how far the drift went. Here the kernel derivation is +# an input, so a mismatched pair is not representable. +{ pkgs, kernelsmith }: + +{ kernel, src, version, target }: + +kernelsmith.buildModule { + name = "igloo-${version}-${target}"; + inherit version src kernel; + + # igloo_driver's Makefile is not a bare `obj-m :=` file. Its default target + # generates two headers (portal_tramp_gen.h, ffi_stubs_generated.h) with + # python3 and only then re-enters kbuild. Driving kbuild directly would skip + # the codegen and fail on the missing headers. + entry = "wrapper"; + + nativeBuildInputs = [ pkgs.python3 ]; + + # The upstream Makefile lives in src/; everything it references is relative to + # it ($(src)/portal, $(src)/../scripts). + preBuild = "cd src"; + + # NB: 32-bit powerpc needs arch/powerpc/lib/crtsavres.o staged into the module + # build directory. That is handled generically in kernelsmith's buildModule -- + # it is a property of ppc32 kbuild, not of this module. _in_container_build.sh + # carries the same workaround, plus an EXTRA_LDFLAGS="-L…" that does nothing: + # KBUILD_LDFLAGS_MODULE names the object positionally, and -L only affects -l. +} diff --git a/nix/kernel.nix b/nix/kernel.nix new file mode 100644 index 0000000..665fd6d --- /dev/null +++ b/nix/kernel.nix @@ -0,0 +1,233 @@ +# One IGLOO kernel cell: (version, target) -> vmlinux + boot image + kernel-devel. +# +# This is a faithful port of _in_container_build.sh's per-target build, with the +# toolchain resolved by kernelsmith instead of an unpinned musl.cc download. +# +# Three outputs: +# out -- the arch's boot artifact, Module.symvers, and (mips*/powerpc* +# only, matching build.sh) a STRIPPED vmlinux +# dev -- the kernel-devel tree out-of-tree modules build against +# vmlinux -- the UNSTRIPPED vmlinux, for osi/cosi extraction +# +# The vmlinux split is not tidiness. _in_container_build.sh runs the osi/cosi +# extractors against the build-tree vmlinux and only THEN strips the copy it +# ships -- an ordering a derivation cannot reproduce, because by the time +# anything downstream sees the kernel it is already realised. Stripping in place +# would leave the analysis derivations reading a vmlinux with no debug info, on +# exactly the mips*/powerpc* targets that ship one. +# +# Named `vmlinux` and not `debug`: `debug` is a name nixpkgs' multiple-outputs +# machinery attaches meaning to, and this file already lost a day to `dev` vs +# `devel` (see below). +# +# The dev output MUST be called "dev": nixpkgs' multiple-outputs setup hook +# relocates include/ to `outputDev`, which falls back to "out" when no output is +# literally named "dev". Naming it "devel" therefore silently moved include/ +# into $out and produced a devel tree that could not build a module. +# +# The `dev` output is the point of the whole exercise: igloo_driver consumes +# it as a DERIVATION INPUT, so a different kernel is a different hash and a +# stale-CRC .ko cannot be produced. Note kernelsmith's own buildKernel installs +# `headers_install` output as kernel-devel -- those are UAPI headers, NOT the +# modules_prepare build tree. Hence this builds its own (see draft 34, Slice 3: +# upstream a build-tree output + buildModule to kernelsmith). +{ pkgs, kernelsmith }: + +let + inherit (pkgs) lib; + + # linux_builder TARGET -> kernel ARCH=, matching _in_container_build.sh's + # short_arch derivation (strip trailing el/eb, then collapse families). + shortArch = { + armel = "arm"; + arm64 = "arm64"; + mipseb = "mips"; + mipsel = "mips"; + mips64eb = "mips"; + mips64el = "mips"; + powerpc = "powerpc"; + powerpcle = "powerpc"; + powerpc64 = "powerpc"; + powerpc64le = "powerpc"; + loongarch64 = "loongarch"; + riscv64 = "riscv"; + x86_64 = "x86_64"; + }; + + # Extra make target beyond vmlinux, and where the artifact lands / ships as. + # arm64 deliberately ships Image.gz under the name zImage.arm64 -- preserving + # the existing consumer-visible naming, quirk and all. + bootArtifact = { + armel = { target = "zImage"; src = "arch/arm/boot/zImage"; dst = "zImage"; }; + arm64 = { target = "Image.gz"; src = "arch/arm64/boot/Image.gz"; dst = "zImage"; }; + x86_64 = { target = "bzImage"; src = "arch/x86/boot/bzImage"; dst = "bzImage"; }; + loongarch64 = { target = "vmlinuz.efi"; src = "arch/loongarch/boot/vmlinuz.efi"; dst = "vmlinuz.efi"; }; + riscv64 = { target = "Image"; src = "arch/riscv/boot/Image"; dst = "Image"; }; + }; + + # vmlinux is the deliverable boot artifact for these families. + deliversVmlinux = target: lib.hasPrefix "mips" target || lib.hasPrefix "powerpc" target; + + # Which arch's TOOLCHAIN a target builds with, where that differs from the + # target itself. + # + # The whole powerpc family builds with ONE biarch powerpc64 big-endian + # compiler, exactly as _in_container_build.sh's get_cc does (every powerpc* + # target there resolves to powerpc64-linux-musl-, or powerpc64-linux-gnu- on + # 4.10). Bitness and endianness come from the kernel's own arch Makefile + # driven by Kconfig -- NOT from the triple. + # + # This is not cosmetic. kernelsmith models the four powerpc variants as four + # independent arches with four separate toolchains, and the per-variant + # powerpc64LE toolchain is 64-bit only: + # + # powerpc64 (BE, Bootlin power8): -m32 OK -m64 OK -mlittle/-mbig OK + # powerpc64le (LE, Bootlin power8): -m32 FAIL + # + # 6.13/powerpc64le sets CONFIG_COMPAT, so kbuild builds a 32-bit vDSO + # (VDSO32A ... sigtramp32-32.o) and the LE-only compiler dies with + # "cc1: error: '-m32' not supported in this configuration". Aligning the + # family to powerpc64 fixes that and drops a from-source musl-cross-make + # build for powerpcle, which Bootlin has no toolchain for at all. + # + # TODO(kernelsmith): this belongs upstream as a kernel-specific resolver + # (`kernelToolchainFor`), NOT as a change to `toolchainFor` -- userland musl + # for powerpc64le should still be the powerpc64le triple. Kept local until + # that API exists. + toolchainArch = target: + if lib.hasPrefix "powerpc" target then "powerpc64" else target; + +in +{ version, target, src, config }: + +let + arch = shortArch.${target} or (throw "kernel.nix: no ARCH mapping for target ${target}"); + boot = bootArtifact.${target} or null; + toolchain = kernelsmith.toolchainFor version (toolchainArch target); + crossPrefix = "${toolchain.target}-"; + + # Trailing -Wno-error beats any -Werror the tree injects, at any depth. + # Same technique kernelsmith's kernel.nix uses; it subsumes the ad-hoc + # KCFLAGS/HOSTCFLAGS juggling _in_container_build.sh does for 4.10/powerpc. + ccShim = pkgs.runCommand "igloo-ccshim-${target}" { } '' + mkdir -p $out/bin + for n in gcc cc; do + if [ -x ${toolchain}/bin/${crossPrefix}$n ]; then + printf '#!%s\nexec %s/bin/%s%s "$@" -Wno-error\n' \ + ${pkgs.runtimeShell} ${toolchain} ${crossPrefix} "$n" > $out/bin/${crossPrefix}$n + chmod +x $out/bin/${crossPrefix}$n + fi + done + ''; + +in +pkgs.stdenv.mkDerivation { + pname = "igloo-kernel-${version}"; + inherit version; + name = "igloo-kernel-${version}-${target}"; + + outputs = [ "out" "dev" "vmlinux" ]; + dontUnpack = true; + enableParallelBuilding = true; + + nativeBuildInputs = with pkgs; [ + ccShim toolchain + gnumake bc bison flex perl python3 rsync cpio kmod which + openssl elfutils pkg-config ubootTools util-linux zstd + ]; + + buildPhase = '' + runHook preBuild + export ARCH=${arch} + export CROSS_COMPILE=${crossPrefix} + export KBUILD_BUILD_TIMESTAMP="@0" + export KBUILD_BUILD_USER=nix + export KBUILD_BUILD_HOST=nix + + cp -r ${src} linux && chmod -R u+w linux + mkdir -p build + + # Old trees vs. the Nix sandbox. Both of these work in the Docker build only + # because an Ubuntu image happens to have the paths; neither is an IGLOO + # change, so they are fixed here in the builder rather than in the patch + # series (which must stay a faithful description of the fork branch). + # + # 4.10's Makefile validates KBUILD_OUTPUT with `cd $dir && /bin/pwd`, and + # there is no /bin/pwd in the sandbox -- it fails with the profoundly + # unhelpful "failed to create output directory". + sed -i 's|/bin/pwd|pwd|g' linux/Makefile + # Kbuild helpers carry shebangs like #!/usr/bin/awk that don't exist here; + # unpatched they fail "not found" and cascade into Kconfig syntax errors. + patchShebangs linux/scripts linux/tools 2>/dev/null || true + + echo ">>> .config (cpp-assembled fragment + olddefconfig)" + cp ${config} build/.config + make -C linux O=$PWD/build olddefconfig + + echo ">>> vmlinux ${lib.optionalString (boot != null) boot.target}" + make -C linux O=$PWD/build vmlinux ${lib.optionalString (boot != null) boot.target} -j$NIX_BUILD_CORES + + echo ">>> modules_prepare + modules (Module.symvers)" + make -C linux O=$PWD/build modules_prepare + make -C linux O=$PWD/build modules -j$NIX_BUILD_CORES + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mkdir -p $out + + # The unstripped vmlinux always goes to its own output -- osi/cosi need the + # debug info, and nothing downstream can un-strip it later. + mkdir -p $vmlinux + cp build/vmlinux $vmlinux/vmlinux.${target} + + # $out gets a vmlinux ONLY where it is the deliverable boot artifact, which + # is what build.sh does; every other target ships just its boot image. + ${lib.optionalString (deliversVmlinux target) '' + cp build/vmlinux $out/vmlinux.${target} + ${crossPrefix}strip $out/vmlinux.${target} || true + ''} + ${lib.optionalString (boot != null) '' + cp build/${boot.src} $out/${boot.dst}.${target} + ''} + cp build/Module.symvers $out/ + + # --- kernel-devel: the modules_prepare result, source+build merged ------- + # Port of _in_container_build.sh's minimal-devel staging. An out-of-tree + # build (make -C $KDIR M=$PWD modules) needs Makefile/.config/Module.symvers, + # headers, arch Makefiles and scripts/ host tools -- not boot images or the + # bulk of tools/. + D=$dev + mkdir -p $D + cp build/.config build/Module.symvers $D/ + cp linux/Makefile linux/Kconfig $D/ || true + cp -r linux/include $D/ 2>/dev/null || true + cp -r build/include $D/ 2>/dev/null || true + mkdir -p $D/arch + for a in ${arch} ${lib.optionalString (arch == "x86_64") "x86"}; do + cp -r linux/arch/$a $D/arch/ 2>/dev/null || true + cp -r build/arch/$a $D/arch/ 2>/dev/null || true + done + cp -r linux/scripts $D/ 2>/dev/null || true + cp -r build/scripts $D/ 2>/dev/null || true + cp -r linux/tools $D/ 2>/dev/null || true + cp -r build/tools $D/ 2>/dev/null || true + + chmod -R u+w $D + # Slim: boot images and realmode are never read by a module build. + rm -rf $D/arch/*/boot $D/arch/*/realmode || true + # Keep tools/objtool (kbuild may run it on module objects); drop the rest. + if [ -d $D/tools ]; then + find $D/tools -mindepth 1 -maxdepth 1 ! -name objtool -exec rm -rf {} + || true + fi + runHook postInstall + ''; + + passthru = { inherit toolchain crossPrefix arch target version; }; + + meta = { + description = "IGLOO kernel ${version} for ${target} (kernelsmith toolchain, patch-series source)"; + platforms = [ "x86_64-linux" ]; + }; +} diff --git a/nix/lint.nix b/nix/lint.nix new file mode 100644 index 0000000..079f543 --- /dev/null +++ b/nix/lint.nix @@ -0,0 +1,83 @@ +# Config linting -- the nix replacement for `./build.sh --config-only`. +# +# What the Docker path did: cpp-assemble configs//, run +# `savedefconfig` against the kernel tree, drop the result at +# config__.linted, and print a diff. That diff was always +# non-empty and its exit status was discarded (`|| true`) -- savedefconfig +# prunes options that are already the default and de-duplicates, so a config +# and its savedefconfig form never match. It was an ADVISORY tool for the +# author of a config change, never a gate, and it is kept as one here. +# +# What it is good for: seeing which lines in a config fragment are redundant +# (already the arch default) or duplicated, before committing them. +# +# Deliberately NOT wired into `nix flake check`. Making it a gate would mean +# asserting that our configs equal their savedefconfig form, which is false for +# every config in this repo by construction, and "fixing" that would mean +# replacing readable fragments with savedefconfig output and losing the +# #include structure that makes configs/ maintainable. +{ pkgs }: + +let + inherit (pkgs) lib; +in +rec { + # One cell's lint. Reuses the kernel's own tree and toolchain via passthru, + # so the linted output comes from exactly the compiler and source that build + # the shipped kernel -- savedefconfig's answer depends on both. + forCell = + { kernel, config, src, version, target }: + pkgs.runCommand "igloo-config-lint-${version}-${target}" + { + nativeBuildInputs = with pkgs; [ + # stdenv.cc is the HOST compiler, and it is required: Kbuild builds + # scripts/basic/fixdep and the Kconfig binaries natively before it + # touches a single target file. `runCommand` is stdenvNoCC in current + # nixpkgs, so without this the lint dies on "gcc: command not found" + # long before reaching savedefconfig. The cross toolchain below is + # prefixed (${kernel.passthru.crossPrefix}gcc), so the two never clash. + stdenv.cc + kernel.passthru.toolchain + gnumake bc bison flex perl python3 rsync which openssl elfutils + ]; + meta.description = "savedefconfig lint for ${version}/${target}"; + } + '' + export ARCH=${kernel.passthru.arch} + export CROSS_COMPILE=${kernel.passthru.crossPrefix} + cp -r ${src} linux && chmod -R u+w linux + mkdir -p build out + + # Same two sandbox fixups the kernel build needs; see nix/kernel.nix. + sed -i 's|/bin/pwd|pwd|g' linux/Makefile + patchShebangs linux/scripts linux/tools 2>/dev/null || true + + cp ${config} build/.config + make -C linux O=$PWD/build olddefconfig >/dev/null + make -C linux O=$PWD/build savedefconfig >/dev/null + + mkdir -p $out + cp build/defconfig $out/config_${version}_${target}.linted + + # Advisory, exactly as the Docker path had it: report, never fail. + { + echo "=== ${version}/${target}: assembled .config vs savedefconfig ===" + echo "Lines only in the assembled config are redundant or defaulted." + diff -u <(sort build/.config) \ + <(sort build/defconfig | sed '/^[ #]/d') || true + } > $out/config_${version}_${target}.diff + cat $out/config_${version}_${target}.diff + ''; + + # Every cell's lint in one tree, so a config sweep is a single build. + all = { cells }: + pkgs.linkFarm "igloo-config-lint" + (map + (c: { + name = "${c.version}-${c.target}"; + path = forCell { + inherit (c) kernel config src version target; + }; + }) + cells); +} diff --git a/nix/matrix.nix b/nix/matrix.nix new file mode 100644 index 0000000..25cb347 --- /dev/null +++ b/nix/matrix.nix @@ -0,0 +1,40 @@ +# The build matrix: kernel version -> targets. +# +# Targets are the linux_builder TARGET names (which are also kernelsmith's arch +# keys, deliberately -- they were already aligned). Sourced from configs//, +# excluding *.inc (fragments) and *.unused (retired targets). +# +# NOTE 4.10 is 7 targets, not 12: every powerpc* config is .unused, and +# loongarch64/riscv64 don't exist for that version. The full matrix is 19 cells, +# not the 24 a naive 2x12 suggests. +# +# powerpcle is retired on BOTH versions. CPU_LITTLE_ENDIAN depends on +# PPC_BOOK3S_64, so a 32-bit little-endian powerpc kernel is not expressible in +# mainline Linux; the config's request was silently dropped and the cell built a +# byte-identical copy of `powerpc`. See configs/6.13/powerpcle.unused. +{ + "4.10" = [ + "armel" + "arm64" + "mipseb" + "mipsel" + "mips64eb" + "mips64el" + "x86_64" + ]; + + "6.13" = [ + "armel" + "arm64" + "mipseb" + "mipsel" + "mips64eb" + "mips64el" + "powerpc" + "powerpc64" + "powerpc64le" + "loongarch64" + "riscv64" + "x86_64" + ]; +} diff --git a/nix/perf.nix b/nix/perf.nix new file mode 100644 index 0000000..aa18ec1 --- /dev/null +++ b/nix/perf.nix @@ -0,0 +1,218 @@ +# perf. -- the statically-linked guest perf binary shipped in +# kernels-latest.tar.gz. +# +# A faithful port of _in_container_build.sh's perf step, with ONE deliberate +# behavioural change: this fails loudly. +# +# The shell runs the build with `|| echo "Warning: Failed to build perf ..."` +# and then guards the copy with `[ -f "$PERF_SRC" ]`, so an arch whose perf does +# not compile silently ships without one. The result is that +# rehosting/penguin:latest carries perf for exactly 3 of 13 targets (armel, +# loongarch64, mips64el) and nobody had to notice. Here a broken arch breaks the +# build, so "this arch has no perf" has to be a decision someone writes down +# rather than a build-day accident. +# +# perf links -static against a TARGET LIBC, which is why loongarch64 cannot use +# the kernel-only gcc that builds its kernel (see kernelsmith's +# matrix.k6LoongarchKernel -- kernel-only means no libc at all). It uses the +# nixpkgs glibc cross instead, whose triple +# (loongarch64-unknown-linux-gnu-) is byte-identical to the hand-installed +# /opt/cross toolchain the Docker image reaches for. So loongarch64 is the one +# arch where the kernel and its perf are built by different compilers -- exactly +# as in production, just written down. +{ pkgs, kernelsmith }: + +let + inherit (pkgs) lib; + + # NB: deliberately NO powerpc family collapsing here, unlike kernel.nix. + # + # kernel.nix routes all four powerpc variants through one biarch powerpc64 BE + # toolchain, which is correct for a kernel: it is built -nostdinc + # -ffreestanding, and arch/powerpc/Makefile derives -m32/-m64 and the + # endianness from Kconfig, so one compiler yields four different kernels. + # + # perf has no Kconfig and links -static against a TARGET LIBC, so neither of + # those holds. Collapsing the family here produced four BYTE-IDENTICAL + # big-endian 64-bit binaries for powerpc, powerpcle, powerpc64 and + # powerpc64le -- three of which cannot run on their guest at all. It fails + # silently: every cell builds, `perf` exists, and only the ELF header shows + # the damage. + # + # Using each variant's own toolchain also gets the matching 32-bit/LE musl, + # which a 64-bit BE sysroot simply does not contain. + # + # (`powerpcle` used to need a special case here, because its kernel came out + # big-endian whatever its name said. The target is retired instead -- see + # configs/6.13/powerpcle.unused -- so the identity mapping is now honest for + # every cell in the matrix.) + toolchainArch = target: target; + + # perf resolves its tools headers with -I$(srctree)/tools/arch/$(ARCH)/include/uapi, + # using ARCH verbatim rather than the SRCARCH that kbuild derives from it. The + # kernel build takes ARCH=x86_64 and normalises it to x86 internally; perf does + # not, and tools/arch/x86_64/ does not exist, so 4.10 fails with + # tools/include/uapi/linux/mman.h:4: fatal error: uapi/asm/mman.h: No such file + # which names the generic header rather than the missing arch directory. + # tools/arch/x86/ is correct for every kernel version, so map it here. + perfArch = a: if a == "x86_64" then "x86" else a; + + # mips64 needs an explicit output-format flag or ld picks the wrong ABI. + extraLdFlags = { + mips64el = " -m elf64ltsmip"; + mips64eb = " -m elf64btsmip"; + }; + + # The NO_* soup, verbatim from the shell. perf pulls in a large optional + # dependency surface; every one of these is off in the shipped build, so a + # difference here would be a silently different binary. + noFlags = [ + "NO_LIBELF" "NO_LIBUNWIND" "NO_LIBNUMA" "NO_LIBAUDIT" + "NO_LIBBIONIC" "NO_LIBPYTHON" "NO_LIBPERL" "NO_SLANG" "NO_LZMA" + "NO_ZLIB" "NO_LIBBPF" "NO_JVMTI" "NO_LIBCRYPTO" "NO_LIBZSTD" + "NO_LIBTRACEEVENT" "NO_AUXTRACE" "NO_CORESIGHT" + ]; + + extraCFlags = lib.concatStringsSep " " [ + "-Wno-error" "-fcommon" "-D__always_inline=inline" + "-Wno-redundant-decls" "-Wno-format-truncation" + "-Wno-format-overflow" "-Wno-array-bounds" + ]; + + # perf probes for optional libc features by compiling test programs under + # tools/build/feature. Cross-compiling makes several of those tests fail for + # reasons that have nothing to do with whether the feature exists, and perf + # responds by defining its own fallback -- which then collides with the real + # declaration the target libc does provide: + # + # bench/bench.h:66 conflicting types for 'pthread_attr_setaffinity_np' + # builtin-record.c:207 static declaration of 'gettid' follows non-static + # + # Both symbols are present in glibc, so the honest fix is to tell perf the + # truth rather than silence the warning. Only the glibc target needs this; + # the musl/kernel-only toolchains really do lack them, and there perf's + # fallback is correct. + glibcFeatureFlags = lib.concatStringsSep " " [ + "-DHAVE_PTHREAD_ATTR_SETAFFINITY_NP" + "-DHAVE_GETTID" + ]; + +in +{ version, target, src, arch }: + +let + isLoong = target == "loongarch64"; + + # loongarch64: nixpkgs glibc cross (has a libc). Everything else: the same + # kernelsmith toolchain that built the kernel. + loongCross = pkgs.pkgsCross.loongarch64-linux; + + # Use the WRAPPED cross cc, not the bare gcc: the wrapper is what puts the + # target glibc on the include and library search paths. With the bare gcc the + # compile succeeds and the link fails on -lpthread/-lrt/-lm/-ldl, which reads + # like a missing dependency but is really a missing sysroot. + toolchain = + if isLoong then loongCross.stdenv.cc + else kernelsmith.toolchainFor version (toolchainArch target); + + # -static needs the archive halves of glibc, which nixpkgs splits into a + # separate `static` output. + loongLibs = lib.optionals isLoong [ + loongCross.buildPackages.binutils + loongCross.stdenv.cc.libc.static + ]; + + crossPrefix = + if isLoong then loongCross.stdenv.cc.targetPrefix + else "${toolchain.target}-"; + + ldFlag = extraLdFlags.${target} or ""; + +in +pkgs.stdenv.mkDerivation { + name = "igloo-perf-${version}-${target}"; + dontUnpack = true; + enableParallelBuilding = true; + + nativeBuildInputs = [ toolchain ] ++ loongLibs ++ (with pkgs; [ + gnumake bison flex perl python3 pkg-config which + ]); + + buildPhase = '' + runHook preBuild + cp -r ${src} linux && chmod -R u+w linux + patchShebangs linux/scripts linux/tools 2>/dev/null || true + + # Same sandbox breakage kernel.nix hits, in a different file: 4.10-era + # tools/scripts/Makefile.include validates OUTPUT with `cd $dir && /bin/pwd`, + # and there is no /bin/pwd here. It reports it as + # *** output directory "/build/out/" does not exist. Stop. + # which points at the wrong thing entirely -- the directory is right there. + # Not an IGLOO change, so it is fixed in the builder, not the patch series. + find linux/tools linux/Makefile -name 'Makefile*' -o -name '*.mk' 2>/dev/null \ + | xargs -r sed -i 's|/bin/pwd|pwd|g' + sed -i 's|/bin/pwd|pwd|g' linux/Makefile + + mkdir -p out +${lib.optionalString (ldFlag != "") '' + # mips64: the musl toolchain's ld defaults to the n32 emulation + # (elf32-ntradlittlemips) while the objects are n64, so relocatable links + # inside libapi fail with "ABI is incompatible with that of the selected + # emulation". + # + # Passing LD="ld -m elf64ltsmip" on the make command line is not enough: + # tools/perf/Makefile does `unexport MAKEFLAGS`, so command-line variables + # do NOT reach the nested tools/lib/* builds, and those are exactly where + # the failure is. A PATH shim survives that, because every one of those + # builds resolves $(CROSS_COMPILE)ld through PATH. + # + # A shim rather than LDEMULATION= because the environment variable would + # also be picked up by the HOST ld that builds fixdep. + mkdir -p ldshim + cat > ldshim/${crossPrefix}ld </` +# (penguin untars it into /igloo_static/, and +# src/penguin/utils.py globs /igloo_static/kernels/*/) +# kernel-devel-all.tar.gz entries under `././` +# +# Tarballs are built reproducibly (sorted, epoch mtimes, numeric root owner, +# gzip -n). The Docker build stamps `Built by linux_builder on $(date)` into +# README.txt, which alone would make every archive byte-different; the date is +# dropped rather than faked, and the provenance that matters -- the store path +# each artifact came from -- is recorded instead. +# +# perf. is built for EVERY cell -- see nix/perf.nix. build.sh swallows +# perf build failures, so rehosting/penguin:latest ships perf for only 3 of 13 +# targets (armel, loongarch64, mips64el) without that ever being a decision. +# Here a failing arch fails the build instead. +{ pkgs }: + +let + inherit (pkgs) lib; + + # tar flags that make an archive a function of its contents only. + reproTar = "--sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner"; + +in +rec { + # One version's worth of /kernels/: boot artifacts + analysis output. + kernelsDir = { version, cells }: + pkgs.runCommand "igloo-kernels-dir-${version}" { } '' + mkdir -p $out + ${lib.concatMapStringsSep "\n" + (c: '' + # Boot artifacts (zImage./bzImage./Image./vmlinuz.efi./vmlinux.). + # Module.symvers is a build input, not a shipped artifact -- skip it. + for f in ${c.kernel}/*; do + b=$(basename "$f") + [ "$b" = "Module.symvers" ] && continue + cp -a "$f" $out/"$b" + done + cp ${c.osi} $out/osi.${c.target}.config + cp ${c.cosi} $out/cosi.${c.target}.json.xz + cp ${c.perf}/perf.${c.target} $out/perf.${c.target} + '') + cells} + + # Aggregate profile, concatenated in a STABLE order. The shell relies on + # glob order, which is locale-dependent; sort explicitly. + for f in $(ls $out/osi.*.config | sort); do cat "$f" >> $out/osi.config; done + ''; + + # The tarball's payload as a plain directory: `/`, which is + # exactly the layout penguin lays down at /igloo_static/kernels/. + # + # Exposed separately so a Nix consumer can take the tree directly instead of + # the archive. penguin's flake currently pins the release TARBALL + # (`inputs.kernels`, flake = false) and relies on Nix unpacking it; going + # through kernelsTarball from another flake would mean tar-then-untar of + # ~335 MB to reproduce a directory this already has. Same contents either way + # -- kernelsTarball is defined in terms of this. + kernelsTree = { versions }: + pkgs.runCommand "igloo-kernels" { } '' + mkdir -p $out + ${lib.concatMapStringsSep "\n" + (v: ''cp -a ${v.dir} $out/${v.version}'') + versions} + chmod -R u+w $out + + # Deliberately not the Docker build's `Built by linux_builder on $(date)`: + # a timestamp would make the archive non-reproducible for no benefit. + # Written with printf rather than a heredoc so the Nix indentation of this + # file does not end up inside the shipped file. + printf '%s\n' \ + 'Built by linux_builder (nix + patch series).' \ + "" \ + 'Provenance is the store path of each input, not a build date -- these' \ + 'artifacts are a pure function of the pinned kernel tarball, the patch' \ + 'series in patches/, the config in configs/, and the kernelsmith toolchain.' \ + > $out/README.txt + ''; + + kernelsTarball = { versions }: + pkgs.runCommand "kernels-latest.tar.gz" { nativeBuildInputs = [ pkgs.gzip ]; } '' + mkdir -p stage + cp -a ${kernelsTree { inherit versions; }} stage/kernels + chmod -R u+w stage + tar ${reproTar} -cf - -C stage kernels | gzip -9n > $out + ''; + + develTarball = { cells }: + pkgs.runCommand "kernel-devel-all.tar.gz" { nativeBuildInputs = [ pkgs.gzip ]; } '' + mkdir -p stage + ${lib.concatMapStringsSep "\n" + # The Docker layout is ., not /. + (c: ''cp -a ${c.kernel.dev} stage/${c.target}.${c.version}'') + cells} + chmod -R u+w stage + tar ${reproTar} -cf - -C stage . | gzip -9n > $out + ''; +} diff --git a/nix/shape.nix b/nix/shape.nix new file mode 100644 index 0000000..0cdbc2b --- /dev/null +++ b/nix/shape.nix @@ -0,0 +1,91 @@ +# Assert that what a cell actually produced matches what its target name claims. +# +# Two bugs on this branch were invisible to every other check because the build +# succeeded and the artifact existed: +# +# - perf inherited kernel.nix's powerpc family collapse and emitted four +# BYTE-IDENTICAL big-endian 64-bit binaries for powerpc, powerpcle, +# powerpc64 and powerpc64le. Three cannot run on their guest. +# +# - configs/6.13/powerpcle asks for CONFIG_CPU_LITTLE_ENDIAN=y, which +# arch/powerpc/platforms/Kconfig.cputype makes conditional on PPC_BOOK3S_64. +# olddefconfig drops it without complaint and the kernel comes out +# big-endian, byte-identical to powerpc's. +# +# Both are the same shape of failure: an ELF whose class or endianness silently +# disagrees with its name. The kernel build cannot catch it (Kconfig resolved +# "correctly" by its own rules) and neither can a smoke test that only checks a +# file exists. Reading the ELF header does catch it, costs nothing, and is the +# check that would have found both. +# +# Deliberately NOT derived from the toolchain or the config -- those are the +# things being checked. The expectation comes from the target name, which is +# what every consumer downstream believes. +{ pkgs }: + +let + inherit (pkgs) lib; + + # target -> (ELF class, byte order), as the NAME promises. + expect = { + armel = { bits = 32; endian = "LSB"; }; + arm64 = { bits = 64; endian = "LSB"; }; + mipsel = { bits = 32; endian = "LSB"; }; + mipseb = { bits = 32; endian = "MSB"; }; + mips64el = { bits = 64; endian = "LSB"; }; + mips64eb = { bits = 64; endian = "MSB"; }; + powerpc = { bits = 32; endian = "MSB"; }; + powerpc64 = { bits = 64; endian = "MSB"; }; + powerpc64le = { bits = 64; endian = "LSB"; }; + loongarch64 = { bits = 64; endian = "LSB"; }; + riscv64 = { bits = 64; endian = "LSB"; }; + x86_64 = { bits = 64; endian = "LSB"; }; + + # No powerpcle entry, and none is needed: the target is retired (see + # configs/6.13/powerpcle.unused) precisely because no honest expectation + # could be written for it. If it ever comes back without real upstream + # ppc32-LE support, it lands in the SKIP branch below rather than passing + # quietly -- which is the point. + }; + +in +{ cells }: + +pkgs.runCommand "igloo-shape-check" +{ + nativeBuildInputs = [ pkgs.file ]; +} '' + fail=0 + report() { printf '%-24s %-9s %s\n' "$1" "$2" "$3"; } + + ${lib.concatMapStringsSep "\n" + (c: + let e = expect.${c.target} or null; in + if e == null then '' + report "${c.version}/${c.target}" "SKIP" "no honest expectation for this target name" + '' else '' + # The kernel comes from the `vmlinux` output, not `out`. Only some + # targets deliver a vmlinux in $out (the rest ship zImage/bzImage/Image, + # which are compressed blobs `file` cannot read a class or byte order + # from), so checking $out would silently skip armel, arm64, loongarch64, + # riscv64 and x86_64 -- five of thirteen, and exactly the kind of + # partial coverage this check exists to prevent. + for f in ${c.kernel.vmlinux}/vmlinux.${c.target} ${c.perf}/perf.${c.target}; do + [ -f "$f" ] || { report "${c.version}/${c.target}" "MISSING" "$f"; fail=1; continue; } + desc=$(file -b "$f") + case "$desc" in + *"${toString e.bits}-bit ${e.endian}"*) report "${c.version}/${c.target}" "ok" "$(basename $f)" ;; + *) + report "${c.version}/${c.target}" "MISMATCH" "$(basename $f): want ${toString e.bits}-bit ${e.endian}, got: $desc" + fail=1 ;; + esac + done + '') + cells} + + if [ $fail -ne 0 ]; then + echo "shape check FAILED: an artifact's ELF class/endianness disagrees with its target name" >&2 + exit 1 + fi + echo "shape check passed" > $out +'' diff --git a/nix/source.nix b/nix/source.nix new file mode 100644 index 0000000..d0b1157 --- /dev/null +++ b/nix/source.nix @@ -0,0 +1,49 @@ +# Kernel source = PRISTINE UPSTREAM TARBALL + the IGLOO patch series. +# +# This is the change that removes the submodules. Previously the source was two +# long-lived fork branches of rehosting/linux, pinned by SHA in .gitmodules -- +# and .gitmodules named the WRONG branches (it declared main_6.7 for linux/6.13 +# while the pin was actually main_6.13, a tree 89,775 commits apart). Basing on +# a real upstream tag makes that class of drift unrepresentable. +# +# Each version's series file lists patch paths relative to patches/, so core +# patches and per-version adapters can interleave in a defined order: +# +# patches/6.13/series +# core/0001-add-hypercall.h.patch +# 6.13/0001-syscall_wrapper-add-x86-support.patch +{ pkgs }: + +let + inherit (pkgs) lib; + + # "4.10" -> "v4.x", "6.13" -> "v6.x" + seriesDir = version: "v${lib.versions.major version}.x"; + + # Read a series file into an ordered list of patch paths, ignoring blank lines + # and # comments (quilt convention). + readSeries = + patchesRoot: version: + let + file = "${patchesRoot}/${version}/series"; + lines = lib.splitString "\n" (builtins.readFile file); + keep = l: l != "" && !(lib.hasPrefix "#" l); + in + map (l: "${patchesRoot}/${l}") (builtins.filter keep (map lib.trim lines)); + +in +{ + inherit readSeries; + + # bases: { "4.10" = { tag = "4.10"; hash = "sha256-..."; }; ... } + kernelSource = + { patchesRoot, version, base }: + pkgs.applyPatches { + name = "linux-${base.tag}-igloo"; + src = pkgs.fetchurl { + url = "https://cdn.kernel.org/pub/linux/kernel/${seriesDir base.tag}/linux-${base.tag}.tar.xz"; + inherit (base) hash; + }; + patches = readSeries patchesRoot version; + }; +} diff --git a/patches/4.10/0001-Net-disallow-changing-bridge-mac-addrs-from-firmadyn.patch b/patches/4.10/0001-Net-disallow-changing-bridge-mac-addrs-from-firmadyn.patch new file mode 100644 index 0000000..648c8fd --- /dev/null +++ b/patches/4.10/0001-Net-disallow-changing-bridge-mac-addrs-from-firmadyn.patch @@ -0,0 +1,26 @@ +From 45ce3c636a9d7cc71d2107b557168cd07de7838f Mon Sep 17 00:00:00 2001 +From: Andrew Fasano +Date: Mon, 18 Dec 2023 09:59:33 -0500 +Subject: [PATCH 01/38] Net: disallow changing bridge mac addrs (from + firmadyne. Unnecessary?) + +--- + net/core/dev.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/core/dev.c b/net/core/dev.c +index 29101c9839..36c24d6100 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6595,6 +6595,11 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa) + const struct net_device_ops *ops = dev->netdev_ops; + int err; + ++ if (dev->priv_flags & IFF_EBRIDGE) { ++ //Changing bridge mac-addrs only causes issues ++ return 0; ++ } ++ + if (!ops->ndo_set_mac_address) + return -EOPNOTSUPP; + if (sa->sa_family != dev->type) diff --git a/patches/4.10/0003-reboot.c-add-igloo_block_halt.patch b/patches/4.10/0003-reboot.c-add-igloo_block_halt.patch new file mode 100644 index 0000000..8ba93d8 --- /dev/null +++ b/patches/4.10/0003-reboot.c-add-igloo_block_halt.patch @@ -0,0 +1,66 @@ +From c5703859c7164c76dd2f875b00a378abe91a9b6a Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 17 Mar 2025 16:02:48 -0400 +Subject: [PATCH 03/38] reboot.c: add igloo_block_halt + +--- + kernel/reboot.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/kernel/reboot.c b/kernel/reboot.c +index bd30a973fe..c1a451d401 100644 +--- a/kernel/reboot.c ++++ b/kernel/reboot.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + /* + * this indicates whether you can reboot with ctrl-alt-del: the default is yes +@@ -213,6 +214,10 @@ void migrate_to_reboot_cpu(void) + */ + void kernel_restart(char *cmd) + { ++ if (igloo_block_halt) { ++ printk("IGLOO: refusing to restart\n"); ++ return; ++ } + kernel_restart_prepare(cmd); + migrate_to_reboot_cpu(); + syscore_shutdown(); +@@ -240,6 +245,10 @@ static void kernel_shutdown_prepare(enum system_states state) + */ + void kernel_halt(void) + { ++ if (igloo_block_halt) { ++ printk("IGLOO: refusing to halt\n"); ++ return; ++ } + kernel_shutdown_prepare(SYSTEM_HALT); + migrate_to_reboot_cpu(); + syscore_shutdown(); +@@ -256,6 +265,10 @@ EXPORT_SYMBOL_GPL(kernel_halt); + */ + void kernel_power_off(void) + { ++ if (igloo_block_halt) { ++ printk("IGLOO: refusing to power off\n"); ++ return; ++ } + kernel_shutdown_prepare(SYSTEM_POWER_OFF); + if (pm_power_off_prepare) + pm_power_off_prepare(); +@@ -284,6 +297,11 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, + char buffer[256]; + int ret = 0; + ++ if (igloo_block_halt) { ++ printk("IGLOO: refusing to reboot\n"); ++ return -EPERM; ++ } ++ + /* We only trust the superuser with rebooting the system. */ + if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) + return -EPERM; diff --git a/patches/4.10/0004-socket.c-add-igloo_socket_hc.patch b/patches/4.10/0004-socket.c-add-igloo_socket_hc.patch new file mode 100644 index 0000000..b1003cc --- /dev/null +++ b/patches/4.10/0004-socket.c-add-igloo_socket_hc.patch @@ -0,0 +1,55 @@ +From 6ad9f96f9f4634b8c4c80ff76faedb28de50c72a Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Tue, 29 Jul 2025 13:39:22 -0400 +Subject: [PATCH 04/38] socket.c: add igloo_socket_hc + +--- + net/socket.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/net/socket.c b/net/socket.c +index 0758e13754..75aa88e267 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -582,6 +582,9 @@ struct socket *sock_alloc(void) + } + EXPORT_SYMBOL(sock_alloc); + ++// forward declare igloo_sock_release ++void igloo_sock_release(struct socket *sock); ++ + /** + * sock_release - close a socket + * @sock: socket to close +@@ -593,6 +596,7 @@ EXPORT_SYMBOL(sock_alloc); + + void sock_release(struct socket *sock) + { ++ igloo_sock_release(sock); + if (sock->ops) { + struct module *owner = sock->ops->owner; + +@@ -1388,6 +1392,9 @@ SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol, + return err; + } + ++// forward declare igloo_sock_bind ++void igloo_sock_bind(struct socket *sock, struct sockaddr_storage *address); ++ + /* + * Bind a name to a socket. Nothing much to do here since it's + * the protocol's responsibility to handle the local address. +@@ -1409,10 +1416,12 @@ SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen) + err = security_socket_bind(sock, + (struct sockaddr *)&address, + addrlen); +- if (!err) ++ if (!err){ + err = sock->ops->bind(sock, + (struct sockaddr *) + &address, addrlen); ++ igloo_sock_bind(sock, address); ++ } + } + fput_light(sock->file, fput_needed); + } diff --git a/patches/4.10/0006-open.c-add-igloo_open_hc.patch b/patches/4.10/0006-open.c-add-igloo_open_hc.patch new file mode 100644 index 0000000..e3336fd --- /dev/null +++ b/patches/4.10/0006-open.c-add-igloo_open_hc.patch @@ -0,0 +1,36 @@ +From 41af0e8a2447242bdbaedbfc119af83842619a61 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 17 Mar 2025 15:13:19 -0400 +Subject: [PATCH 06/38] open.c: add igloo_open_hc + +--- + fs/open.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/fs/open.c b/fs/open.c +index 9921f70bc5..309a355ef8 100644 +--- a/fs/open.c ++++ b/fs/open.c +@@ -1035,6 +1035,11 @@ struct file *filp_clone_open(struct file *oldfile) + } + EXPORT_SYMBOL(filp_clone_open); + ++#ifdef CONFIG_IGLOO ++// forward declare for igloo_hc_open ++void igloo_hc_open(int dfd, struct filename *tmp, int fd); ++#endif ++ + long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) + { + struct open_flags op; +@@ -1048,6 +1053,10 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + ++#ifdef CONFIG_IGLOO ++ igloo_hc_open(dfd, tmp, fd); ++#endif ++ + fd = get_unused_fd_flags(flags); + if (fd >= 0) { + struct file *f = do_filp_open(dfd, tmp, &op); diff --git a/patches/4.10/0007-sys.c-add-igloo_hc_newuname.patch b/patches/4.10/0007-sys.c-add-igloo_hc_newuname.patch new file mode 100644 index 0000000..ce8d5d6 --- /dev/null +++ b/patches/4.10/0007-sys.c-add-igloo_hc_newuname.patch @@ -0,0 +1,39 @@ +From 70e12bb9d545ba3a5e20f7091285aee7aa20e059 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Tue, 29 Jul 2025 13:39:56 -0400 +Subject: [PATCH 07/38] sys.c: add igloo_hc_newuname + +--- + kernel/sys.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/kernel/sys.c b/kernel/sys.c +index 842914ef7d..f019b9cd50 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1138,15 +1138,23 @@ static int override_release(char __user *release, size_t len) + return ret; + } + ++// forward declare make_igloo_utsname ++void igloo_hc_newuname(struct new_utsname *name); ++ + SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + { + int errno = 0; ++ struct new_utsname tmp; + + down_read(&uts_sem); +- if (copy_to_user(name, utsname(), sizeof *name)) +- errno = -EFAULT; ++ memcpy(&tmp, utsname(), sizeof(tmp)); + up_read(&uts_sem); + ++ igloo_hc_newuname(&tmp); ++ ++ if (copy_to_user(name, utsname(), sizeof *name)) ++ errno = -EFAULT; ++ + if (!errno && override_release(name->release, sizeof(name->release))) + errno = -EFAULT; + if (!errno && override_architecture(name)) diff --git a/patches/4.10/0008-namespace.c-add-igloo_should_block_mount.patch b/patches/4.10/0008-namespace.c-add-igloo_should_block_mount.patch new file mode 100644 index 0000000..7d357b2 --- /dev/null +++ b/patches/4.10/0008-namespace.c-add-igloo_should_block_mount.patch @@ -0,0 +1,38 @@ +From 9b98cc9d5fc5e6b641c829cb5213ac05d1ae8bc7 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Tue, 29 Jul 2025 13:41:02 -0400 +Subject: [PATCH 08/38] namespace.c: add igloo_should_block_mount + +--- + fs/namespace.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/fs/namespace.c b/fs/namespace.c +index 487ba30bb5..5c4c3fdbb9 100644 +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -2721,6 +2721,11 @@ char *copy_mount_string(const void __user *data) + return data ? strndup_user(data, PAGE_SIZE) : NULL; + } + ++#ifdef CONFIG_IGLOO ++// forward declare igloo_should_block_mount ++bool igloo_should_block_mount(struct path *path); ++#endif ++ + /* + * Flags is a 32-bit value that allows up to 31 non-fs dependent flags to + * be given to the mount() call (ie: read-only, no-dev, no-suid etc). +@@ -2754,6 +2759,12 @@ long do_mount(const char *dev_name, const char __user *dir_name, + retval = user_path(dir_name, &path); + if (retval) + return retval; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_should_block_mount(&path)) { ++ return 0; ++ } ++#endif + + retval = security_sb_mount(dev_name, &path, + type_page, flags, data_page); diff --git a/patches/4.10/0009-ioctl.c-add-igloo_ioctl_hc.patch b/patches/4.10/0009-ioctl.c-add-igloo_ioctl_hc.patch new file mode 100644 index 0000000..ef8f990 --- /dev/null +++ b/patches/4.10/0009-ioctl.c-add-igloo_ioctl_hc.patch @@ -0,0 +1,37 @@ +From ef26bd547c0c66380faab13ef3c9eac27a07f5e8 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 17 Mar 2025 15:12:52 -0400 +Subject: [PATCH 09/38] ioctl.c: add igloo_ioctl_hc + +--- + fs/ioctl.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/fs/ioctl.c b/fs/ioctl.c +index cb9b029408..de77adea04 100644 +--- a/fs/ioctl.c ++++ b/fs/ioctl.c +@@ -612,6 +612,11 @@ static int ioctl_file_dedupe_range(struct file *file, void __user *arg) + return ret; + } + ++#ifdef CONFIG_IGLOO ++// forward declare igloo_ioctl ++void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user * argp); ++#endif ++ + /* + * When you add any new common ioctls to the switches above and below + * please update compat_sys_ioctl() too. +@@ -683,6 +688,11 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, + error = vfs_ioctl(filp, cmd, arg); + break; + } ++ ++#ifdef CONFIG_IGLOO ++ igloo_ioctl(error, inode, filp, cmd, argp); ++#endif ++ + return error; + } + diff --git a/patches/4.10/0010-igloobase-add-driver.patch b/patches/4.10/0010-igloobase-add-driver.patch new file mode 100644 index 0000000..454eeba --- /dev/null +++ b/patches/4.10/0010-igloobase-add-driver.patch @@ -0,0 +1,723 @@ +From c52cb61c3939876165143f28b07634bc918f13ac Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:10:02 -0400 +Subject: [PATCH 10/38] igloobase: add driver + +--- + drivers/Kconfig | 2 + + drivers/Makefile | 3 + + drivers/igloobase/Kconfig | 5 + + drivers/igloobase/Makefile | 2 + + drivers/igloobase/igloo_args.c | 118 ++++++++++ + drivers/igloobase/igloo_weaksyms.c | 112 +++++++++ + drivers/igloobase/igloobase.c | 28 +++ + drivers/igloobase/igloobase.h | 2 + + drivers/igloobase/igloobasehypercalls.h | 6 + + drivers/igloobase/osi_notifier.c | 55 +++++ + drivers/igloobase/syscalls_info_report.c | 281 +++++++++++++++++++++++ + 11 files changed, 614 insertions(+) + create mode 100644 drivers/igloobase/Kconfig + create mode 100644 drivers/igloobase/Makefile + create mode 100644 drivers/igloobase/igloo_args.c + create mode 100644 drivers/igloobase/igloo_weaksyms.c + create mode 100644 drivers/igloobase/igloobase.c + create mode 100644 drivers/igloobase/igloobase.h + create mode 100644 drivers/igloobase/igloobasehypercalls.h + create mode 100644 drivers/igloobase/osi_notifier.c + create mode 100644 drivers/igloobase/syscalls_info_report.c + +diff --git a/drivers/Kconfig b/drivers/Kconfig +index e1e2066cec..cc7f36cfff 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig" + + source "drivers/fpga/Kconfig" + ++source "drivers/igloobase/Kconfig" ++ + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index 060026a02f..a299da7780 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -173,3 +173,6 @@ obj-$(CONFIG_STM) += hwtracing/stm/ + obj-$(CONFIG_ANDROID) += android/ + obj-$(CONFIG_NVMEM) += nvmem/ + obj-$(CONFIG_FPGA) += fpga/ ++ ++# Added ++obj-y += igloobase/ +\ No newline at end of file +diff --git a/drivers/igloobase/Kconfig b/drivers/igloobase/Kconfig +new file mode 100644 +index 0000000000..b20c5df509 +--- /dev/null ++++ b/drivers/igloobase/Kconfig +@@ -0,0 +1,5 @@ ++config IGLOO ++ bool "IGLOO module support" ++ default y ++ help ++ Support for IGLOO analysis +\ No newline at end of file +diff --git a/drivers/igloobase/Makefile b/drivers/igloobase/Makefile +new file mode 100644 +index 0000000000..1865aadfbb +--- /dev/null ++++ b/drivers/igloobase/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_IGLOO) += osi_notifier.o syscalls_info_report.o \ ++ igloobase.o igloo_weaksyms.o igloo_args.o +\ No newline at end of file +diff --git a/drivers/igloobase/igloo_args.c b/drivers/igloobase/igloo_args.c +new file mode 100644 +index 0000000000..bdb65d1fbc +--- /dev/null ++++ b/drivers/igloobase/igloo_args.c +@@ -0,0 +1,118 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "igloo_syscall_macros.h" ++#include "igloo.h" ++ ++ ++/** ++ * Early params originally from igloo_hc.c in the module ++ */ ++unsigned long igloo_task_size = 0; ++static int __init early_igloo_task_size(char *p) ++{ ++ unsigned long task_size; ++ if (kstrtoul(p, 0, &task_size) < 0 ) { ++ pr_warn("Could not parse igloo_task_size parameter %s\n", p); ++ return -1; ++ } ++ igloo_task_size = task_size; ++ pr_warn_once("Using igloo_task_size: 0x%lx\n", igloo_task_size); ++ return 0; ++} ++early_param("igloo_task_size", early_igloo_task_size); ++EXPORT_SYMBOL(igloo_task_size); ++ ++bool igloo_block_halt=false; ++ ++static int __init early_igloo_block_halt(char *p) ++{ ++ unsigned long block_halt; ++ if (kstrtoul(p, 0, &block_halt) < 0 ) { ++ pr_warn("Could not parse igloo_block_halt parameter %s. Set to 0 (default) or 1\n", p); ++ return -1; ++ } ++ igloo_block_halt = (block_halt > 0); ++ pr_warn_once("Using igloo_block_halt: %d\n", igloo_block_halt); ++ return 0; ++} ++ ++early_param("igloo_block_halt", early_igloo_block_halt); ++EXPORT_SYMBOL(igloo_block_halt); ++ ++// Debug logging configuration for each module ++struct igloo_debug_config { ++ bool portal; // Enable debug for portal module ++ bool uprobe; // Enable debug for uprobe module ++ bool vma; // Enable debug for VMA tracking ++ bool syscall; // Enable debug for syscall tracking ++ bool osi; // Enable debug for OSI features ++}; ++ ++// Global debug configuration ++struct igloo_debug_config igloo_debug = { ++ .portal = false, ++ .uprobe = false, ++ .vma = false, ++ .syscall = false, ++ .osi = false, ++}; ++ ++// Parse comma-separated list of modules to enable debug logging for ++static int __init early_igloo_debug_modules(char *p) ++{ ++ char *token; ++ ++ // By default, all modules have debug disabled ++ memset(&igloo_debug, 0, sizeof(igloo_debug)); ++ ++ // Special case: "all" enables all modules ++ if (!strcmp(p, "all")) { ++ memset(&igloo_debug, 1, sizeof(igloo_debug)); ++ pr_warn_once("IGLOO: Debug enabled for all modules\n"); ++ return 0; ++ } ++ ++ // Special case: "none" disables all modules (default) ++ if (!strcmp(p, "none")) { ++ memset(&igloo_debug, 0, sizeof(igloo_debug)); ++ pr_warn_once("IGLOO: Debug disabled for all modules\n"); ++ return 0; ++ } ++ ++ // Parse comma-separated module list ++ while ((token = strsep(&p, ",")) != NULL) { ++ if (!strcmp(token, "portal")) ++ igloo_debug.portal = true; ++ else if (!strcmp(token, "uprobe")) ++ igloo_debug.uprobe = true; ++ else if (!strcmp(token, "vma")) ++ igloo_debug.vma = true; ++ else if (!strcmp(token, "syscall")) ++ igloo_debug.syscall = true; ++ else if (!strcmp(token, "osi")) ++ igloo_debug.osi = true; ++ else if (!strcmp(token, "all")){ ++ memset(&igloo_debug, 1, sizeof(igloo_debug)); ++ pr_warn_once("IGLOO: Debug enabled for all modules\n"); ++ return 0; ++ } ++ else ++ pr_warn("IGLOO: Unknown debug module: %s\n", token); ++ } ++ ++ pr_warn_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d\n", ++ igloo_debug.portal, igloo_debug.uprobe, igloo_debug.vma, ++ igloo_debug.syscall, igloo_debug.osi); ++ ++ return 0; ++} ++ ++early_param("igloo_debug", early_igloo_debug_modules); ++EXPORT_SYMBOL(igloo_debug); +\ No newline at end of file +diff --git a/drivers/igloobase/igloo_weaksyms.c b/drivers/igloobase/igloo_weaksyms.c +new file mode 100644 +index 0000000000..8498d15500 +--- /dev/null ++++ b/drivers/igloobase/igloo_weaksyms.c +@@ -0,0 +1,112 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "igloo_syscall_macros.h" ++#include "igloo.h" ++ ++/* Syscall hooks */ ++igloo_syscall_enter_t igloo_syscall_enter_hook = NULL; ++igloo_syscall_return_t igloo_syscall_return_hook = NULL; ++EXPORT_SYMBOL(igloo_syscall_enter_hook); ++EXPORT_SYMBOL(igloo_syscall_return_hook); ++ ++// Function pointer for override ++bool (*igloo_should_block_mount_module)(struct path *path); ++bool igloo_should_block_mount(struct path *path); ++bool igloo_should_block_mount(struct path *path) ++{ ++ if (igloo_should_block_mount_module) { ++ return igloo_should_block_mount_module(path); ++ } else { ++ // printk(KERN_INFO "igloo_should_block_mount: default implementation called\n"); ++ return false; ++ } ++} ++EXPORT_SYMBOL(igloo_should_block_mount); ++ ++void (*igloo_sock_release_module)(struct socket *sock); ++void igloo_sock_release(struct socket *sock) ++{ ++ if (igloo_sock_release_module) { ++ igloo_sock_release_module(sock); ++ } else { ++ // printk(KERN_EMERG "igloo_sock_release: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_sock_release); ++ ++void (*igloo_sock_bind_module)(struct socket *sock, struct sockaddr_storage *address); ++void igloo_sock_bind(struct socket *sock, struct sockaddr_storage *address) ++{ ++ if (igloo_sock_bind_module) { ++ igloo_sock_bind_module(sock, address); ++ } else { ++ // printk(KERN_EMERG "igloo_sock_bind: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_sock_bind); ++ ++void (*igloo_hc_newuname_module)(struct new_utsname *name) = NULL; ++void igloo_hc_newuname(struct new_utsname *name) ++{ ++ if (igloo_hc_newuname_module) { ++ igloo_hc_newuname_module(name); ++ } else { ++ // printk(KERN_EMERG "igloo_hc_newuname: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_hc_newuname); ++ ++void (*igloo_hc_open_module)(int dfd, const char __user *filename, struct open_how *how); ++void igloo_hc_open(int dfd, const char __user *filename, struct open_how *how); ++void igloo_hc_open(int dfd, const char __user *filename, struct open_how *how) ++{ ++ if (igloo_hc_open_module) { ++ igloo_hc_open_module(dfd, filename, how); ++ } else { ++ // printk(KERN_EMERG "igloo_hc_open: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_hc_open); ++ ++void (*igloo_ioctl_module)(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp); ++void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp); ++void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp) ++{ ++ if (igloo_ioctl_module) { ++ igloo_ioctl_module(error, inode, filp, cmd, argp); ++ } else { ++ // printk(KERN_EMERG "igloo_ioctl: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_ioctl); ++ ++/* Export internal symbols needed for introspection research */ ++ ++// Symbol lookup functions - now pulled in by trace/syscall.h ++EXPORT_SYMBOL(kallsyms_lookup); ++ ++extern unsigned long kallsyms_lookup_name(const char *name); ++EXPORT_SYMBOL(kallsyms_lookup_name); ++ ++// Architecture-specific functions ++extern const char *arch_vma_name(struct vm_area_struct *vma); ++EXPORT_SYMBOL(arch_vma_name); ++ ++// Process management ++extern pid_t kernel_clone(struct kernel_clone_args *args); ++EXPORT_SYMBOL(kernel_clone); ++ ++extern int kill_pid_info(int sig, struct kernel_siginfo *info, struct pid *pid); ++EXPORT_SYMBOL(kill_pid_info); ++ ++// Memory access ++extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, ++ void *buf, int len, unsigned int gup_flags); ++EXPORT_SYMBOL(access_remote_vm); +\ No newline at end of file +diff --git a/drivers/igloobase/igloobase.c b/drivers/igloobase/igloobase.c +new file mode 100644 +index 0000000000..005835633c +--- /dev/null ++++ b/drivers/igloobase/igloobase.c +@@ -0,0 +1,28 @@ ++#include ++#include ++#include ++#include "igloo.h" ++#include "igloobase.h" ++ ++ ++/* Register probes for mmap and munmap */ ++static int __init igloo_base_init(void) { ++ printk(KERN_EMERG "IGLOOBase: Initializing\n"); ++ int ret = 0; ++ if ((ret = osi_notifier_init()) != 0) { ++ printk(KERN_ERR "Failed to register osi_notifier_init\n"); ++ } ++ if ((ret = syscalls_info_report()) != 0) { ++ printk(KERN_ERR "Failed to register syscalls_hc returning %d\n", ret); ++ } ++ return 0; ++} ++ ++/* Unregister probes */ ++static void __exit igloo_base_exit(void) { ++ // Unreachable, module is built in ++ printk(KERN_ERR "TODO\n"); ++} ++ ++module_init(igloo_base_init); ++module_exit(igloo_base_exit); +\ No newline at end of file +diff --git a/drivers/igloobase/igloobase.h b/drivers/igloobase/igloobase.h +new file mode 100644 +index 0000000000..3fe72b89a0 +--- /dev/null ++++ b/drivers/igloobase/igloobase.h +@@ -0,0 +1,2 @@ ++int osi_notifier_init(void); ++int syscalls_info_report(void); +\ No newline at end of file +diff --git a/drivers/igloobase/igloobasehypercalls.h b/drivers/igloobase/igloobasehypercalls.h +new file mode 100644 +index 0000000000..f7fa8b985e +--- /dev/null ++++ b/drivers/igloobase/igloobasehypercalls.h +@@ -0,0 +1,6 @@ ++// This should be a relatively small file as most functionality should be ++// implemented in the core igloo driver ++ ++enum igloo_base_hypercalls { ++ IGLOO_HYP_SETUP_SYSCALL = 0x1337, ++}; +\ No newline at end of file +diff --git a/drivers/igloobase/osi_notifier.c b/drivers/igloobase/osi_notifier.c +new file mode 100644 +index 0000000000..b9ed55d799 +--- /dev/null ++++ b/drivers/igloobase/osi_notifier.c +@@ -0,0 +1,55 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "hypercall.h" ++#include ++#include ++#include ++#include ++#include /* Needed by all modules */ ++#include /* Needed for KERN_INFO */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "igloo.h" ++#include "igloobase.h" ++ ++#define IGLOO_HYP_OSI_TASK_SWITCH 0x3337 ++ ++// Define a tracepoint probe function for sched_switch with the correct signature ++static void probe_sched_switch(void *data, bool preempt, struct task_struct *prev, ++ struct task_struct *next, unsigned int prev_state) ++{ ++ // Notify hypervisor about task switch using task pointers ++ igloo_hypercall2(IGLOO_HYP_OSI_TASK_SWITCH, (unsigned long)prev, (unsigned long)next); ++} ++ ++int osi_notifier_init(void) { ++ int ret = 0; ++ ++ // Register the sched_switch tracepoint ++ ret = register_trace_sched_switch(probe_sched_switch, NULL); ++ if (ret) { ++ printk(KERN_ERR "IGLOO: Failed to register sched_switch tracepoint, returned %d\n", ret); ++ } else { ++ printk(KERN_INFO "IGLOO: Successfully registered sched_switch tracepoint\n"); ++ } ++ return 0; ++} +\ No newline at end of file +diff --git a/drivers/igloobase/syscalls_info_report.c b/drivers/igloobase/syscalls_info_report.c +new file mode 100644 +index 0000000000..9927c02e3e +--- /dev/null ++++ b/drivers/igloobase/syscalls_info_report.c +@@ -0,0 +1,281 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "hypercall.h" // Content is now included directly below ++#include "igloo.h" ++#include "igloobase.h" ++#include "igloobasehypercalls.h" ++ ++extern struct syscall_metadata *__start_syscalls_metadata[]; ++extern struct syscall_metadata *__stop_syscalls_metadata[]; ++ ++#ifndef ARCH_HAS_SYSCALL_MATCH_SYM_NAME ++static inline bool arch_syscall_match_sym_name(const char *sym, const char *name) ++{ ++ /* ++ * Only compare after the "sys" prefix. Archs that use ++ * syscall wrappers may have syscalls symbols aliases prefixed ++ * with ".SyS" or ".sys" instead of "sys", leading to an unwanted ++ * mismatch. ++ */ ++ return !strcmp(sym + 3, name + 3); ++} ++#endif ++ ++/* Normalize syscall names by removing common prefixes like 'sys_', '_sys_', 'compat_sys_' */ ++static inline const char *normalize_syscall_name(const char *name) ++{ ++ if (!name) ++ return NULL; ++ ++ /* Skip leading underscores (e.g. _sys_) */ ++ while (*name == '_') ++ name++; ++ ++ /* Check for 'sys_' prefix */ ++ if (strncmp(name, "sys_", 4) == 0) ++ return name + 4; ++ ++ /* Check for 'compat_sys_' prefix */ ++ if (strncmp(name, "compat_sys_", 11) == 0) ++ return name + 11; ++ ++ /* Check for other arch-specific prefixes */ ++ if (strncmp(name, "arm64_sys_", 10) == 0) ++ return name + 10; ++ ++ if (strncmp(name, "riscv_sys_", 10) == 0) ++ return name + 10; ++ ++ return name; ++} ++ ++// copied from trace_syscalls.c ++static struct syscall_metadata * ++find_syscall_meta_copy(unsigned long syscall); ++static struct syscall_metadata * ++find_syscall_meta_copy(unsigned long syscall) ++{ ++ struct syscall_metadata **start; ++ struct syscall_metadata **stop; ++ char str[KSYM_SYMBOL_LEN]; ++ ++ ++ start = __start_syscalls_metadata; ++ stop = __stop_syscalls_metadata; ++ kallsyms_lookup(syscall, NULL, NULL, NULL, str); ++ ++ if (arch_syscall_match_sym_name(str, "sys_ni_syscall")) ++ return NULL; ++ ++ for ( ; start < stop; start++) { ++ if ((*start)->name && arch_syscall_match_sym_name(str, (*start)->name)) ++ return *start; ++ } ++ return NULL; ++} ++ ++static void report_syscall(char * buffer, struct syscall_metadata *meta){ ++ if (!meta || !meta->name) { ++ return; // Skip invalid metadata ++ } ++ // Prepare JSON metadata for hypercall (ensure buffer is large enough) ++ int x = snprintf(buffer, PAGE_SIZE, ++ "{\"name\": \"%s\", \"args\":[", ++ normalize_syscall_name(meta->name)); ++ ++ for (int j = 0; j < meta->nb_args && x > 0 && x < PAGE_SIZE; j++) { ++ // Append args safely, checking remaining buffer space ++ x += snprintf((char*)buffer + x, PAGE_SIZE - x, "[\"%s\", \"%s\"]%s", ++ meta->types[j] ? meta->types[j] : "?", // Handle potential NULL type/arg names ++ meta->args[j] ? meta->args[j] : "?", ++ j + 1 < meta->nb_args ? ", " : ""); ++ } ++ ++ if (x > 0 && x < PAGE_SIZE) { ++ x += snprintf((char*)buffer + x, PAGE_SIZE - x, "]}"); ++ } ++ ++ if (x <= 0 || x >= PAGE_SIZE) { ++ // DBG_PRINTK( "IGLOO: Failed to format JSON for syscall %s (nr %d) - buffer overflow or snprintf error.\n", meta->name, meta->syscall_nr); ++ // Decide how to handle: skip this probe or abort? Skipping for now. ++ return; ++ } ++ // Send metadata via hypercall (call returns value, but it's ignored here) ++ igloo_hypercall(IGLOO_HYP_SETUP_SYSCALL, (unsigned long)buffer); ++} ++ ++// normalize_syscall_name is now defined in syscalls_hc.h ++ ++#ifdef CONFIG_COMPAT ++/* For ARM64 */ ++#if defined(CONFIG_ARM64) ++extern const syscall_fn_t compat_sys_call_table[]; ++/* Don't redeclare sys_call_table as it's already in syscall.h with correct type */ ++#define COMPAT_TABLE_SIZE __NR_compat32_syscalls ++ ++/* For x86_64 */ ++// #elif defined(CONFIG_X86_64) ++// /* Use void* instead of syscall_fn_t for broader compatibility */ ++// extern const void * const ia32_sys_call_table[]; ++// #define compat_sys_call_table ia32_sys_call_table ++// #define COMPAT_TABLE_SIZE IA32_NR_syscalls ++ ++/* For MIPS64 */ ++#elif defined(CONFIG_MIPS) && defined(CONFIG_64BIT) ++/* Use the correct declaration that matches what's in syscall.h */ ++#include /* Ensure we get the right declaration */ ++#define compat_sys_call_table sys32_call_table ++#define COMPAT_TABLE_SIZE NR_syscalls /* Use NR_syscalls instead of __NR_syscalls */ ++ ++/* For PPC64 */ ++// #elif defined(CONFIG_PPC64) ++// extern void *sys32_call_table[]; ++// #define compat_sys_call_table sys32_call_table ++// #define COMPAT_TABLE_SIZE __NR_syscalls ++ ++/* For RISC-V64 */ ++#elif defined(CONFIG_RISCV) && defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) ++/* Use the correct declaration for RISC-V - it's already properly declared in syscall.h */ ++#define COMPAT_TABLE_SIZE __NR_syscalls ++#endif ++#endif ++ ++/* Get syscall name from a function pointer */ ++#ifdef CONFIG_COMPAT ++static const char *get_syscall_name_from_func(void *func_ptr) { ++ char sym[KSYM_SYMBOL_LEN]; ++ ++ if (!func_ptr || IS_ERR(func_ptr)) ++ return NULL; ++ ++ kallsyms_lookup((unsigned long)func_ptr, NULL, NULL, NULL, sym); ++ ++ /* Skip if we couldn't identify the symbol */ ++ if (!sym[0]) ++ return NULL; ++ ++ /* Skip the "sys_" or similar prefix */ ++ if (strncmp(sym, "sys_", 4) == 0) ++ return kstrdup(sym, GFP_KERNEL); ++ else if (strncmp(sym, "compat_sys_", 11) == 0) ++ return kstrdup(sym + 7, GFP_KERNEL); /* Return without the "compat_" prefix */ ++ else if (strncmp(sym, "__arm64_", 8) == 0) ++ return kstrdup(sym + 8, GFP_KERNEL); /* Return without the "__arm64_" prefix */ ++ else if (strncmp(sym, "__loongarch_", 12) == 0) ++ return kstrdup(sym + 12, GFP_KERNEL); /* Return without the "__loongarch_" prefix */ ++ else if (strncmp(sym, "__riscv_", 8) == 0) ++ return kstrdup(sym + 8, GFP_KERNEL); /* Return without the "__riscv_" prefix */ ++ else if (strncmp(sym, "__se_", 5) == 0) ++ return kstrdup(sym + 5, GFP_KERNEL); /* Return without the "__se_" prefix */ ++ ++ return kstrdup(sym, GFP_KERNEL); ++} ++ ++static void report_syscall_from_func(char *buffer, void *func_ptr, int syscall_nr) { ++ const char *name; ++ int x; ++ ++ if (!func_ptr || IS_ERR(func_ptr)) ++ return; ++ ++ name = get_syscall_name_from_func(func_ptr); ++ if (!name) ++ return; ++ ++ /* Create a simplified metadata report for compat syscalls */ ++ x = snprintf(buffer, PAGE_SIZE, "{\"name\": \"%s\", \"compat\": true, \"args\":\"unknown\"}", ++ normalize_syscall_name(name)); ++ ++ if (x > 0 && x < PAGE_SIZE) { ++ /* Send this metadata via hypercall */ ++ igloo_hypercall(IGLOO_HYP_SETUP_SYSCALL, (unsigned long)buffer); ++ } ++ ++ kfree(name); ++} ++#endif ++ ++int syscalls_info_report(void) { ++ printk(KERN_EMERG "IGLOO: Initializing syscall hypercalls\n"); ++ struct syscall_metadata **p = __start_syscalls_metadata; ++ struct syscall_metadata **end = __stop_syscalls_metadata; ++ ++ void *buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); ++ ++ if (!buffer) { ++ printk(KERN_ERR "IGLOO: Failed to allocate memory for syscall metadata buffer\n"); ++ return -ENOMEM; ++ } ++ ++ // Count the number of syscalls ++ int num_syscall_probes = end - p; ++ if (num_syscall_probes <= 0) { ++ printk(KERN_WARNING "IGLOO: No syscall metadata found.\n"); ++ return -EINVAL; ++ } ++ ++ // Process regular syscalls first ++ int i; ++ for (i = 0; i < NR_syscalls+1000; i++) { ++ struct syscall_metadata *meta; ++ unsigned long addr; ++ addr = arch_syscall_addr(i); ++ meta = find_syscall_meta_copy(addr); ++ if (!meta) ++ continue; ++ meta->syscall_nr = i; ++ report_syscall(buffer, meta); ++ } ++ ++ for (p = __start_syscalls_metadata; p < end; p++) { ++ struct syscall_metadata *meta = *p; ++ if (!meta) { ++ continue; // Skip invalid metadata ++ } ++ report_syscall(buffer, meta); ++ } ++ ++ // Process compat syscalls ++#ifdef CONFIG_COMPAT ++#ifdef COMPAT_TABLE_SIZE ++ printk(KERN_INFO "IGLOO: Processing compat syscall table with %d entries\n", COMPAT_TABLE_SIZE); ++ ++ for (i = 0; i < COMPAT_TABLE_SIZE; i++) { ++#if defined(CONFIG_RISCV) && defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) ++ /* For RISC-V, use the already declared variable without casting */ ++ void *func_ptr = compat_sys_call_table[i]; ++#elif defined(CONFIG_MIPS) && defined(CONFIG_64BIT) ++ /* For MIPS64, handle the unsigned long array correctly */ ++ void *func_ptr = (void *)(unsigned long)compat_sys_call_table[i]; ++#else ++ /* For other architectures, use proper casting based on architecture pointer size */ ++ void *func_ptr = (void *)(uintptr_t)compat_sys_call_table[i]; ++#endif ++ ++ /* Skip non-existent syscalls (usually NULL) */ ++ if (!func_ptr || IS_ERR(func_ptr)) { ++ continue; ++ } ++ ++ report_syscall_from_func(buffer, func_ptr, i); ++ } ++#else ++ printk(KERN_INFO "IGLOO: No compat syscall table found for this architecture\n"); ++#endif ++#endif ++ ++ kfree(buffer); ++ return 0; ++} +\ No newline at end of file diff --git a/patches/4.10/0011-exec.c-add-igloo_task_size.patch b/patches/4.10/0011-exec.c-add-igloo_task_size.patch new file mode 100644 index 0000000..c9ddb71 --- /dev/null +++ b/patches/4.10/0011-exec.c-add-igloo_task_size.patch @@ -0,0 +1,32 @@ +From 0a80e99b2d992a8a46df90217374f4de03a7f529 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:15:02 -0400 +Subject: [PATCH 11/38] exec.c: add igloo_task_size + +--- + fs/exec.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/fs/exec.c b/fs/exec.c +index e579466107..ab8a94d669 100644 +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -61,6 +61,7 @@ + #include + #include + #include ++#include + + #include + #include "internal.h" +@@ -1323,6 +1324,10 @@ void setup_new_exec(struct linux_binprm * bprm) + * some architectures like powerpc + */ + current->mm->task_size = TASK_SIZE; ++ #ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ current->mm->task_size = igloo_task_size; ++ #endif + + /* install the new credentials */ + if (!uid_eq(bprm->cred->uid, current_euid()) || diff --git a/patches/4.10/0012-mmap.c-add-igloo_task_size.patch b/patches/4.10/0012-mmap.c-add-igloo_task_size.patch new file mode 100644 index 0000000..64cfaa2 --- /dev/null +++ b/patches/4.10/0012-mmap.c-add-igloo_task_size.patch @@ -0,0 +1,36 @@ +From e1c3ea60d03bf7551aa37c08cbea9f52f79c0337 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:16:06 -0400 +Subject: [PATCH 12/38] mmap.c: add igloo_task_size + +--- + arch/x86/mm/mmap.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c +index d2dc0438d6..9ed4b0b5f1 100644 +--- a/arch/x86/mm/mmap.c ++++ b/arch/x86/mm/mmap.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + struct va_alignment __read_mostly va_align = { + .flags = -1, +@@ -89,6 +90,14 @@ static unsigned long mmap_base(unsigned long rnd) + gap = MIN_GAP; + else if (gap > MAX_GAP) + gap = MAX_GAP; ++ ++ #ifdef CONFIG_IGLOO ++ if(igloo_task_size) { ++ return PAGE_ALIGN(igloo_task_size - gap - rnd); ++ } else { ++ return PAGE_ALIGN(TASK_SIZE - gap - rnd); ++ } ++ #endif + + return PAGE_ALIGN(TASK_SIZE - gap - rnd); + } diff --git a/patches/4.10/0014-add-syscalls.h.patch b/patches/4.10/0014-add-syscalls.h.patch new file mode 100644 index 0000000..eeeec4d --- /dev/null +++ b/patches/4.10/0014-add-syscalls.h.patch @@ -0,0 +1,141 @@ +From 0dfb25514522421228bc6a27f4c02a72b955317c Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:22:08 -0400 +Subject: [PATCH 14/38] add syscalls.h + +--- + include/linux/syscalls.h | 111 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 100 insertions(+), 11 deletions(-) + +diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h +index 91a740f6b8..1a7f4688f3 100644 +--- a/include/linux/syscalls.h ++++ b/include/linux/syscalls.h +@@ -175,9 +175,58 @@ extern struct trace_event_functions exit_syscall_print_funcs; + #define SYSCALL_METADATA(sname, nb, ...) + #endif + +-#define SYSCALL_DEFINE0(sname) \ +- SYSCALL_METADATA(_##sname, 0); \ +- asmlinkage long sys_##sname(void) ++#ifdef CONFIG_IGLOO ++#include ++/* === Igloo Interception Hooks and Helpers === */ ++ ++/* Pointers to the actual hook functions */ ++extern igloo_syscall_enter_t igloo_syscall_enter_hook; ++extern igloo_syscall_return_t igloo_syscall_return_hook; ++#else /* CONFIG_IGLOO not defined */ ++ ++/* Define stubs or original macros if Igloo is disabled */ ++#define igloo_syscall_enter_hook NULL ++#define igloo_syscall_return_hook NULL ++#endif /* CONFIG_IGLOO */ ++ ++#define SYSCALL_DEFINE0(sname) \ ++ SYSCALL_METADATA(_##sname, 0); \ ++ asmlinkage long sys_##sname(void) \ ++ { \ ++ const char *syscall_basename = __stringify(name); /* Base name */ \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ /* Array for arguments (empty for 0 args) */ \ ++ unsigned long args_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ \ ++ /* === Igloo Enter Hook === */ \ ++ if (igloo_syscall_enter_hook) \ ++ { \ ++ /* Pass NULL for setter func for 0-arg syscalls */ \ ++ skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, 0, args_array, NULL); \ ++ } \ ++ \ ++ if (skip) \ ++ { \ ++ ret = skip_ret; \ ++ } \ ++ else \ ++ { \ ++ ret = __do_sys_##sname(); \ ++ } \ ++ \ ++ /* === Igloo Return Hook === */ \ ++ if (igloo_syscall_return_hook) \ ++ { \ ++ ret = igloo_syscall_return_hook(syscall_basename, ret, 0, args_array); \ ++ } \ ++ \ ++ /* Argument protection and final return */ \ ++ __PROTECT(0, ret); /* Protect for 0 args */ \ ++ return ret ; \ ++ } \ ++ static inline long __do_sys_##sname(void) + + #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) + #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) +@@ -191,17 +240,57 @@ extern struct trace_event_functions exit_syscall_print_funcs; + __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) + + #define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) +-#define __SYSCALL_DEFINEx(x, name, ...) \ ++#define __SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ + __attribute__((alias(__stringify(SyS##name)))); \ +- static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ +- asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ +- asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { \ ++ __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); \ ++ } \ ++ static inline long SYSC##name(__MAP(x, __SC_DECL, __VA_ARGS__)); \ ++ asmlinkage long SyS##name(__MAP(x, __SC_LONG, __VA_ARGS__)); \ ++ asmlinkage long SyS##name(__MAP(x, __SC_LONG, __VA_ARGS__)) \ + { \ +- long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ +- __MAP(x,__SC_TEST,__VA_ARGS__); \ +- __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ +- return ret; \ ++ const char *syscall_basename = __stringify(name); \ ++ long ret = 0; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ \ ++ /* Populate args_ptr_array with ADDRESSES for the enter hook */ \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_array, __VA_ARGS__); \ ++ \ ++ /* === Igloo Enter Hook === */ \ ++ if (igloo_syscall_enter_hook) { \ ++ /* Pass setter function pointer unconditionally (except for 0 args) */ \ ++ /* The hook is responsible for handling const args correctly. */ \ ++ skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, x, \ ++ args_ptr_array, __igloo_set_args##name); \ ++ } \ ++ \ ++ if (skip) { \ ++ /* Syscall skipped by hook */ \ ++ ret = skip_ret; \ ++ } else { \ ++ /* Execute actual syscall implementation */ \ ++ /* Arguments used here are potentially modified */ \ ++ /* by the enter hook via the setter function */ \ ++ ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ ++ } \ ++ \ ++ /* Original type tests */ \ ++ __MAP(x,__SC_TEST,__VA_ARGS__); \ ++ \ ++ /* === Igloo Return Hook === */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(syscall_basename, ret, x, \ ++ args_ptr_array); \ ++ } \ ++ \ ++ /* Argument protection and final return */ \ ++ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ ++ return ret; \ + } \ + static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + diff --git a/patches/4.10/0015-syscalls.h-fixes.patch b/patches/4.10/0015-syscalls.h-fixes.patch new file mode 100644 index 0000000..8525a31 --- /dev/null +++ b/patches/4.10/0015-syscalls.h-fixes.patch @@ -0,0 +1,67 @@ +From 450f40b0d9bcfbbab7a033395835ed9ab7a1e2c2 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 20 Sep 2025 18:09:59 -0400 +Subject: [PATCH 15/38] syscalls.h: fixes + +--- + include/linux/syscalls.h | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h +index 1a7f4688f3..f7d0ac1d9a 100644 +--- a/include/linux/syscalls.h ++++ b/include/linux/syscalls.h +@@ -191,6 +191,7 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + + #define SYSCALL_DEFINE0(sname) \ + SYSCALL_METADATA(_##sname, 0); \ ++ static inline long __do_sys_##sname(void); \ + asmlinkage long sys_##sname(void) \ + { \ + const char *syscall_basename = __stringify(name); /* Base name */ \ +@@ -198,15 +199,14 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + bool skip = false; \ + long skip_ret = 0; \ + /* Array for arguments (empty for 0 args) */ \ +- unsigned long args_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ + \ + /* === Igloo Enter Hook === */ \ + if (igloo_syscall_enter_hook) \ + { \ + /* Pass NULL for setter func for 0-arg syscalls */ \ +- skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, 0, args_array, NULL); \ ++ skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, 0, args_ptr_array, NULL); \ + } \ +- \ + if (skip) \ + { \ + ret = skip_ret; \ +@@ -219,12 +219,12 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + /* === Igloo Return Hook === */ \ + if (igloo_syscall_return_hook) \ + { \ +- ret = igloo_syscall_return_hook(syscall_basename, ret, 0, args_array); \ ++ ret = igloo_syscall_return_hook(syscall_basename, ret, 0, args_ptr_array); \ + } \ + \ + /* Argument protection and final return */ \ + __PROTECT(0, ret); /* Protect for 0 args */ \ +- return ret ; \ ++ return ret; \ + } \ + static inline long __do_sys_##sname(void) + +@@ -256,10 +256,10 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + long ret = 0; \ + bool skip = false; \ + long skip_ret = 0; \ +- unsigned long args_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ + \ + /* Populate args_ptr_array with ADDRESSES for the enter hook */ \ +- __SC_ASSIGN_ADDR_WRAPPER(x, args_array, __VA_ARGS__); \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ + \ + /* === Igloo Enter Hook === */ \ + if (igloo_syscall_enter_hook) { \ diff --git a/patches/4.10/0016-syscals.h-fix-sname-in-define0.patch b/patches/4.10/0016-syscals.h-fix-sname-in-define0.patch new file mode 100644 index 0000000..81f97e7 --- /dev/null +++ b/patches/4.10/0016-syscals.h-fix-sname-in-define0.patch @@ -0,0 +1,22 @@ +From fb76ed1995743218dc4a0d8ac2e343ffd666b2e3 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 22 Sep 2025 09:04:08 -0400 +Subject: [PATCH 16/38] syscals.h: fix sname in define0 + +--- + include/linux/syscalls.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h +index f7d0ac1d9a..f916d83569 100644 +--- a/include/linux/syscalls.h ++++ b/include/linux/syscalls.h +@@ -194,7 +194,7 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + static inline long __do_sys_##sname(void); \ + asmlinkage long sys_##sname(void) \ + { \ +- const char *syscall_basename = __stringify(name); /* Base name */ \ ++ const char *syscall_basename = __stringify(sname); /* Base name */ \ + long ret; \ + bool skip = false; \ + long skip_ret = 0; \ diff --git a/patches/4.10/0017-binfmt_elf.c-add-igloo_task_size-support.patch b/patches/4.10/0017-binfmt_elf.c-add-igloo_task_size-support.patch new file mode 100644 index 0000000..4d25688 --- /dev/null +++ b/patches/4.10/0017-binfmt_elf.c-add-igloo_task_size-support.patch @@ -0,0 +1,42 @@ +From b404b7c4531145e2f99c36c993c955384b8f8dfc Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:29:33 -0400 +Subject: [PATCH 17/38] binfmt_elf.c: add igloo_task_size support + +--- + fs/binfmt_elf.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c +index 422370293c..af32087f08 100644 +--- a/fs/binfmt_elf.c ++++ b/fs/binfmt_elf.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + + #ifndef user_long_t + #define user_long_t long +@@ -857,8 +858,19 @@ static int load_elf_binary(struct linux_binprm *bprm) + + /* Do this so that we can load the interpreter, if need be. We will + change some of these later */ ++ #ifdef CONFIG_IGLOO ++ //Begin for igloo: if we moved the stack, we have to move mmap ++ if(igloo_task_size) { ++ retval = setup_arg_pages(bprm, randomize_stack_top(igloo_task_size), ++ executable_stack); ++ } else { ++ retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), ++ executable_stack); ++ } ++ #else + retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), +- executable_stack); ++ executable_stack); ++ #endif + if (retval < 0) + goto out_free_dentry; + diff --git a/patches/4.10/0018-compat.h-syscalls-support.patch b/patches/4.10/0018-compat.h-syscalls-support.patch new file mode 100644 index 0000000..3e85330 --- /dev/null +++ b/patches/4.10/0018-compat.h-syscalls-support.patch @@ -0,0 +1,110 @@ +From 1b1fd64f0454df20e119055e9ec0d9a3e19ae682 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:31:18 -0400 +Subject: [PATCH 18/38] compat.h: syscalls support + +--- + include/linux/compat.h | 79 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 77 insertions(+), 2 deletions(-) + +diff --git a/include/linux/compat.h b/include/linux/compat.h +index 63609398ef..8c02fb57e2 100644 +--- a/include/linux/compat.h ++++ b/include/linux/compat.h +@@ -30,8 +30,45 @@ + #define __SC_DELOUSE(t,v) ((t)(unsigned long)(v)) + #endif + ++// === Igloo Interception Support === ++#include ++ ++#ifdef CONFIG_IGLOO ++extern igloo_syscall_enter_t igloo_syscall_enter_hook; ++extern igloo_syscall_return_t igloo_syscall_return_hook; ++#endif ++ + #define COMPAT_SYSCALL_DEFINE0(name) \ +- asmlinkage long compat_sys_##name(void) ++ asmlinkage long compat_sys_##name(void) \ ++ { \ ++ const char *syscall_basename = __stringify(name); /* Base name */ \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ /* Array for arguments (empty for 0 args) */ \ ++ unsigned long args_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ ++ /* === Igloo Enter Hook === */ \ ++ if (igloo_syscall_enter_hook) { \ ++ /* Pass NULL for setter func for 0-arg syscalls */ \ ++ skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, 0, args_array, NULL); \ ++ } \ ++ \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_compat_sys_##name(); \ ++ } \ ++ \ ++ /* === Igloo Return Hook === */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(syscall_basename, ret, 0, args_array); \ ++ } \ ++ \ ++ return ret ; \ ++ } ++ static inline long __do_compat_sys_##name(void) ++ + + #define COMPAT_SYSCALL_DEFINE1(name, ...) \ + COMPAT_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) +@@ -50,10 +87,48 @@ + asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))\ + __attribute__((alias(__stringify(compat_SyS##name)))); \ + static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ ++ /* Setter function definition: Body generated using __SC_GEN_SETTER_BODY_WRAPPER */ \ ++ void __compat_igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __compat_igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { \ ++ __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); \ ++ } + asmlinkage long compat_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));\ + asmlinkage long compat_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))\ + { \ +- return C_SYSC##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ ++ const char *syscall_basename = __stringify(name); /* Base name */ \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ /* Declare ONE array for argument pointers */ \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ \ ++ /* Populate args_ptr_array with ADDRESSES for the enter hook */ \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ \ ++ /* === Igloo Enter Hook === */ \ ++ if (igloo_syscall_enter_hook) { \ ++ /* Pass setter function pointer unconditionally (except for 0 args) */ \ ++ /* The hook is responsible for handling const args correctly. */ \ ++ skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, x, \ ++ args_ptr_array, __compat_igloo_set_args##name); \ ++ } \ ++ if (skip) { \ ++ /* Syscall skipped by hook */ \ ++ ret = skip_ret; \ ++ } else { \ ++ /* Execute actual syscall implementation */ \ ++ /* Arguments used here are potentially modified */ \ ++ /* by the enter hook via the setter function */ \ ++ ret = C_SYSC##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ ++ } \ ++ \ ++ /* === Igloo Return Hook === */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(syscall_basename, ret, x, \ ++ args_ptr_array); \ ++ } \ ++ return ret; \ + } \ + static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + diff --git a/patches/4.10/0019-give-up-on-gcc-ilog2-constant-optimizations.patch b/patches/4.10/0019-give-up-on-gcc-ilog2-constant-optimizations.patch new file mode 100644 index 0000000..ce48318 --- /dev/null +++ b/patches/4.10/0019-give-up-on-gcc-ilog2-constant-optimizations.patch @@ -0,0 +1,124 @@ +From c88c463900ffefdd82a0d149e3c05626f2841101 Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +Date: Thu, 2 Mar 2017 12:17:22 -0800 +Subject: [PATCH 19/38] give up on gcc ilog2() constant optimizations + +gcc-7 has an "optimization" pass that completely screws up, and +generates the code expansion for the (impossible) case of calling +ilog2() with a zero constant, even when the code gcc compiles does not +actually have a zero constant. + +And we try to generate a compile-time error for anybody doing ilog2() on +a constant where that doesn't make sense (be it zero or negative). So +now gcc7 will fail the build due to our sanity checking, because it +created that constant-zero case that didn't actually exist in the source +code. + +There's a whole long discussion on the kernel mailing about how to work +around this gcc bug. The gcc people themselevs have discussed their +"feature" in + + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=72785 + +but it's all water under the bridge, because while it looked at one +point like it would be solved by the time gcc7 was released, that was +not to be. + +So now we have to deal with this compiler braindamage. + +And the only simple approach seems to be to just delete the code that +tries to warn about bad uses of ilog2(). + +So now "ilog2()" will just return 0 not just for the value 1, but for +any non-positive value too. + +It's not like I can recall anybody having ever actually tried to use +this function on any invalid value, but maybe the sanity check just +meant that such code never made it out in public. + +Reported-by: Laura Abbott +Cc: John Stultz , +Cc: Thomas Gleixner +Cc: Ard Biesheuvel +Signed-off-by: Linus Torvalds +--- + include/linux/log2.h | 13 ++----------- + tools/include/linux/log2.h | 13 ++----------- + 2 files changed, 4 insertions(+), 22 deletions(-) + +diff --git a/include/linux/log2.h b/include/linux/log2.h +index ef3d4f6711..c373295f35 100644 +--- a/include/linux/log2.h ++++ b/include/linux/log2.h +@@ -15,12 +15,6 @@ + #include + #include + +-/* +- * deal with unrepresentable constant logarithms +- */ +-extern __attribute__((const, noreturn)) +-int ____ilog2_NaN(void); +- + /* + * non-constant log of base 2 calculators + * - the arch may override these in asm/bitops.h if they can be implemented +@@ -85,7 +79,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) + #define ilog2(n) \ + ( \ + __builtin_constant_p(n) ? ( \ +- (n) < 1 ? ____ilog2_NaN() : \ ++ (n) < 2 ? 0 : \ + (n) & (1ULL << 63) ? 63 : \ + (n) & (1ULL << 62) ? 62 : \ + (n) & (1ULL << 61) ? 61 : \ +@@ -148,10 +142,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) + (n) & (1ULL << 4) ? 4 : \ + (n) & (1ULL << 3) ? 3 : \ + (n) & (1ULL << 2) ? 2 : \ +- (n) & (1ULL << 1) ? 1 : \ +- (n) & (1ULL << 0) ? 0 : \ +- ____ilog2_NaN() \ +- ) : \ ++ 1 ) : \ + (sizeof(n) <= 4) ? \ + __ilog2_u32(n) : \ + __ilog2_u64(n) \ +diff --git a/tools/include/linux/log2.h b/tools/include/linux/log2.h +index 41446668cc..d5677d39c1 100644 +--- a/tools/include/linux/log2.h ++++ b/tools/include/linux/log2.h +@@ -12,12 +12,6 @@ + #ifndef _TOOLS_LINUX_LOG2_H + #define _TOOLS_LINUX_LOG2_H + +-/* +- * deal with unrepresentable constant logarithms +- */ +-extern __attribute__((const, noreturn)) +-int ____ilog2_NaN(void); +- + /* + * non-constant log of base 2 calculators + * - the arch may override these in asm/bitops.h if they can be implemented +@@ -78,7 +72,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) + #define ilog2(n) \ + ( \ + __builtin_constant_p(n) ? ( \ +- (n) < 1 ? ____ilog2_NaN() : \ ++ (n) < 2 ? 0 : \ + (n) & (1ULL << 63) ? 63 : \ + (n) & (1ULL << 62) ? 62 : \ + (n) & (1ULL << 61) ? 61 : \ +@@ -141,10 +135,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) + (n) & (1ULL << 4) ? 4 : \ + (n) & (1ULL << 3) ? 3 : \ + (n) & (1ULL << 2) ? 2 : \ +- (n) & (1ULL << 1) ? 1 : \ +- (n) & (1ULL << 0) ? 0 : \ +- ____ilog2_NaN() \ +- ) : \ ++ 1 ) : \ + (sizeof(n) <= 4) ? \ + __ilog2_u32(n) : \ + __ilog2_u64(n) \ diff --git a/patches/4.10/0020-extern-yylloc-so-gcc-is-happy.patch b/patches/4.10/0020-extern-yylloc-so-gcc-is-happy.patch new file mode 100644 index 0000000..b32ab7d --- /dev/null +++ b/patches/4.10/0020-extern-yylloc-so-gcc-is-happy.patch @@ -0,0 +1,36 @@ +From c3d9eca87969dc8e071252ce410ef15e3679d5ae Mon Sep 17 00:00:00 2001 +From: Andrew Fasano +Date: Fri, 21 Oct 2022 10:29:23 -0400 +Subject: [PATCH 20/38] extern yylloc so gcc is happy + +--- + scripts/dtc/dtc-lexer.l | 2 +- + scripts/dtc/dtc-lexer.lex.c_shipped | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l +index 790fbf6cf2..e7eab4d7c5 100644 +--- a/scripts/dtc/dtc-lexer.l ++++ b/scripts/dtc/dtc-lexer.l +@@ -38,7 +38,7 @@ LINECOMMENT "//".*\n + #include "srcpos.h" + #include "dtc-parser.tab.h" + +-YYLTYPE yylloc; ++extern YYLTYPE yylloc; + extern bool treesource_error; + + /* CAUTION: this will stop working if we ever use yyless() or yyunput() */ +diff --git a/scripts/dtc/dtc-lexer.lex.c_shipped b/scripts/dtc/dtc-lexer.lex.c_shipped +index ba525c2f9f..a2fe8dbc0f 100644 +--- a/scripts/dtc/dtc-lexer.lex.c_shipped ++++ b/scripts/dtc/dtc-lexer.lex.c_shipped +@@ -637,7 +637,7 @@ char *yytext; + #include "srcpos.h" + #include "dtc-parser.tab.h" + +-YYLTYPE yylloc; ++extern YYLTYPE yylloc; + extern bool treesource_error; + + /* CAUTION: this will stop working if we ever use yyless() or yyunput() */ diff --git a/patches/4.10/0021-Fix-build-with-GCC-versions-8-for-some-targets.patch b/patches/4.10/0021-Fix-build-with-GCC-versions-8-for-some-targets.patch new file mode 100644 index 0000000..4a83512 --- /dev/null +++ b/patches/4.10/0021-Fix-build-with-GCC-versions-8-for-some-targets.patch @@ -0,0 +1,24 @@ +From 0978e0e13bc13561ff026dd7989aecf948469d7b Mon Sep 17 00:00:00 2001 +From: Benjamin Levy +Date: Wed, 17 Jan 2024 15:53:36 -0500 +Subject: [PATCH 21/38] Fix build with GCC versions >=8 for some targets + +--- + Makefile | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/Makefile b/Makefile +index f1e6a02a0c..160e3393ac 100644 +--- a/Makefile ++++ b/Makefile +@@ -401,6 +401,10 @@ KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ + -Wno-format-security \ + -std=gnu89 $(call cc-option,-fno-PIE) + ++# IGLOO ++# Fix build with GCC versions >=8 for some targets ++# https://github.com/torvalds/linux/commit/bee2003 ++KBUILD_CFLAGS += -Wno-attribute-alias + + KBUILD_AFLAGS_KERNEL := + KBUILD_CFLAGS_KERNEL := diff --git a/patches/4.10/0022-igloo_weaksyms-fix-igloo_hc_open-type.patch b/patches/4.10/0022-igloo_weaksyms-fix-igloo_hc_open-type.patch new file mode 100644 index 0000000..8cec627 --- /dev/null +++ b/patches/4.10/0022-igloo_weaksyms-fix-igloo_hc_open-type.patch @@ -0,0 +1,29 @@ +From 9e0524b6f487ec769fee9532685d9d62af677248 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 20 Sep 2025 18:08:14 -0400 +Subject: [PATCH 22/38] igloo_weaksyms: fix igloo_hc_open type + +--- + drivers/igloobase/igloo_weaksyms.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/drivers/igloobase/igloo_weaksyms.c b/drivers/igloobase/igloo_weaksyms.c +index 8498d15500..9e7e5c6639 100644 +--- a/drivers/igloobase/igloo_weaksyms.c ++++ b/drivers/igloobase/igloo_weaksyms.c +@@ -63,12 +63,11 @@ void igloo_hc_newuname(struct new_utsname *name) + } + EXPORT_SYMBOL(igloo_hc_newuname); + +-void (*igloo_hc_open_module)(int dfd, const char __user *filename, struct open_how *how); +-void igloo_hc_open(int dfd, const char __user *filename, struct open_how *how); +-void igloo_hc_open(int dfd, const char __user *filename, struct open_how *how) ++void (*igloo_hc_open_module)(int dfd, struct filename *tmp, int fd); ++void igloo_hc_open(int dfd, struct filename *tmp, int fd) + { + if (igloo_hc_open_module) { +- igloo_hc_open_module(dfd, filename, how); ++ igloo_hc_open_module(dfd, tmp, fd); + } else { + // printk(KERN_EMERG "igloo_hc_open: unimplemented\n"); + } diff --git a/patches/4.10/0023-igloo_weaksyms-forward-proper-types-for-4.10.patch b/patches/4.10/0023-igloo_weaksyms-forward-proper-types-for-4.10.patch new file mode 100644 index 0000000..9d15551 --- /dev/null +++ b/patches/4.10/0023-igloo_weaksyms-forward-proper-types-for-4.10.patch @@ -0,0 +1,53 @@ +From c115ca46444956b69960b85abeed3a112d6e17a8 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 20 Sep 2025 18:08:32 -0400 +Subject: [PATCH 23/38] igloo_weaksyms: forward proper types for 4.10 + +--- + drivers/igloobase/igloo_weaksyms.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/drivers/igloobase/igloo_weaksyms.c b/drivers/igloobase/igloo_weaksyms.c +index 9e7e5c6639..3057659f1a 100644 +--- a/drivers/igloobase/igloo_weaksyms.c ++++ b/drivers/igloobase/igloo_weaksyms.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include "igloo_syscall_macros.h" +@@ -91,18 +92,31 @@ EXPORT_SYMBOL(igloo_ioctl); + // Symbol lookup functions - now pulled in by trace/syscall.h + EXPORT_SYMBOL(kallsyms_lookup); + ++ ++// version not confirmed ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0) + extern unsigned long kallsyms_lookup_name(const char *name); + EXPORT_SYMBOL(kallsyms_lookup_name); ++#endif + + // Architecture-specific functions + extern const char *arch_vma_name(struct vm_area_struct *vma); + EXPORT_SYMBOL(arch_vma_name); + ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0) + // Process management + extern pid_t kernel_clone(struct kernel_clone_args *args); + EXPORT_SYMBOL(kernel_clone); ++#else ++// we will need another version here ++#endif + ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,20,0) + extern int kill_pid_info(int sig, struct kernel_siginfo *info, struct pid *pid); ++#else ++extern int kill_pid_info(int sig, struct siginfo *info, struct pid *pid); ++#endif + EXPORT_SYMBOL(kill_pid_info); + + // Memory access diff --git a/patches/4.10/0024-igloobase.c-4.10-compliance.patch b/patches/4.10/0024-igloobase.c-4.10-compliance.patch new file mode 100644 index 0000000..f1ef823 --- /dev/null +++ b/patches/4.10/0024-igloobase.c-4.10-compliance.patch @@ -0,0 +1,23 @@ +From a824f3d1d264a2cccaa724a01f20eaec42174835 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 20 Sep 2025 18:08:44 -0400 +Subject: [PATCH 24/38] igloobase.c: 4.10 compliance + +--- + drivers/igloobase/igloobase.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/igloobase/igloobase.c b/drivers/igloobase/igloobase.c +index 005835633c..7462e9ee4c 100644 +--- a/drivers/igloobase/igloobase.c ++++ b/drivers/igloobase/igloobase.c +@@ -7,8 +7,8 @@ + + /* Register probes for mmap and munmap */ + static int __init igloo_base_init(void) { ++ int ret; + printk(KERN_EMERG "IGLOOBase: Initializing\n"); +- int ret = 0; + if ((ret = osi_notifier_init()) != 0) { + printk(KERN_ERR "Failed to register osi_notifier_init\n"); + } diff --git a/patches/4.10/0025-osi_notifier-4.10-compliance.patch b/patches/4.10/0025-osi_notifier-4.10-compliance.patch new file mode 100644 index 0000000..f7c72bf --- /dev/null +++ b/patches/4.10/0025-osi_notifier-4.10-compliance.patch @@ -0,0 +1,27 @@ +From ececf18586bf592e75f5a044e65fce1eebc672a4 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 20 Sep 2025 18:08:55 -0400 +Subject: [PATCH 25/38] osi_notifier: 4.10 compliance + +--- + drivers/igloobase/osi_notifier.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/igloobase/osi_notifier.c b/drivers/igloobase/osi_notifier.c +index b9ed55d799..9d02d2f8ae 100644 +--- a/drivers/igloobase/osi_notifier.c ++++ b/drivers/igloobase/osi_notifier.c +@@ -34,8 +34,13 @@ + #define IGLOO_HYP_OSI_TASK_SWITCH 0x3337 + + // Define a tracepoint probe function for sched_switch with the correct signature ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,18,0) + static void probe_sched_switch(void *data, bool preempt, struct task_struct *prev, + struct task_struct *next, unsigned int prev_state) ++#else ++static void probe_sched_switch(void *data, bool preempt, struct task_struct *prev, ++ struct task_struct *next) ++#endif + { + // Notify hypervisor about task switch using task pointers + igloo_hypercall2(IGLOO_HYP_OSI_TASK_SWITCH, (unsigned long)prev, (unsigned long)next); diff --git a/patches/4.10/0026-syscalls_info_report-4.10-compliance.patch b/patches/4.10/0026-syscalls_info_report-4.10-compliance.patch new file mode 100644 index 0000000..47dc82f --- /dev/null +++ b/patches/4.10/0026-syscalls_info_report-4.10-compliance.patch @@ -0,0 +1,70 @@ +From 9d99675cfd40640912a06efe548526e0059f8cd6 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 20 Sep 2025 18:09:34 -0400 +Subject: [PATCH 26/38] syscalls_info_report: 4.10 compliance + +--- + drivers/igloobase/syscalls_info_report.c | 24 +++++++++++++----------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +diff --git a/drivers/igloobase/syscalls_info_report.c b/drivers/igloobase/syscalls_info_report.c +index 9927c02e3e..1f447bcc95 100644 +--- a/drivers/igloobase/syscalls_info_report.c ++++ b/drivers/igloobase/syscalls_info_report.c +@@ -87,15 +87,15 @@ find_syscall_meta_copy(unsigned long syscall) + } + + static void report_syscall(char * buffer, struct syscall_metadata *meta){ ++ int x, j; + if (!meta || !meta->name) { + return; // Skip invalid metadata + } + // Prepare JSON metadata for hypercall (ensure buffer is large enough) +- int x = snprintf(buffer, PAGE_SIZE, ++ x = snprintf(buffer, PAGE_SIZE, + "{\"name\": \"%s\", \"args\":[", + normalize_syscall_name(meta->name)); +- +- for (int j = 0; j < meta->nb_args && x > 0 && x < PAGE_SIZE; j++) { ++ for (j = 0; j < meta->nb_args && x > 0 && x < PAGE_SIZE; j++) { + // Append args safely, checking remaining buffer space + x += snprintf((char*)buffer + x, PAGE_SIZE - x, "[\"%s\", \"%s\"]%s", + meta->types[j] ? meta->types[j] : "?", // Handle potential NULL type/arg names +@@ -207,27 +207,29 @@ static void report_syscall_from_func(char *buffer, void *func_ptr, int syscall_n + } + #endif + +-int syscalls_info_report(void) { +- printk(KERN_EMERG "IGLOO: Initializing syscall hypercalls\n"); +- struct syscall_metadata **p = __start_syscalls_metadata; +- struct syscall_metadata **end = __stop_syscalls_metadata; +- +- void *buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); ++int __init syscalls_info_report(void) { ++ struct syscall_metadata **p, **end; // Fix declaration ++ int num_syscall_probes, i; // Move declarations to top ++ void *buffer; + ++ p = __start_syscalls_metadata; ++ end = __stop_syscalls_metadata; ++ ++ buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); ++ + if (!buffer) { + printk(KERN_ERR "IGLOO: Failed to allocate memory for syscall metadata buffer\n"); + return -ENOMEM; + } + + // Count the number of syscalls +- int num_syscall_probes = end - p; ++ num_syscall_probes = end - p; + if (num_syscall_probes <= 0) { + printk(KERN_WARNING "IGLOO: No syscall metadata found.\n"); + return -EINVAL; + } + + // Process regular syscalls first +- int i; + for (i = 0; i < NR_syscalls+1000; i++) { + struct syscall_metadata *meta; + unsigned long addr; diff --git a/patches/4.10/0027-socket.c-add-config_igloo.patch b/patches/4.10/0027-socket.c-add-config_igloo.patch new file mode 100644 index 0000000..6f7cb24 --- /dev/null +++ b/patches/4.10/0027-socket.c-add-config_igloo.patch @@ -0,0 +1,35 @@ +From 757a088b0305f5aa512b02150ed01a60206ccf89 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 20 Sep 2025 18:09:48 -0400 +Subject: [PATCH 27/38] socket.c: add config_igloo + +--- + net/socket.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/net/socket.c b/net/socket.c +index 75aa88e267..47ffdfc03a 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -1392,8 +1392,10 @@ SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol, + return err; + } + ++#ifdef CONFIG_IGLOO + // forward declare igloo_sock_bind + void igloo_sock_bind(struct socket *sock, struct sockaddr_storage *address); ++#endif + + /* + * Bind a name to a socket. Nothing much to do here since it's +@@ -1420,7 +1422,9 @@ SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen) + err = sock->ops->bind(sock, + (struct sockaddr *) + &address, addrlen); +- igloo_sock_bind(sock, address); ++ #ifdef CONFIG_IGLOO ++ igloo_sock_bind(sock, &address); ++ #endif + } + } + fput_light(sock->file, fput_needed); diff --git a/patches/4.10/0029-igloo_weaksyms-fix-up-passed-arguments.patch b/patches/4.10/0029-igloo_weaksyms-fix-up-passed-arguments.patch new file mode 100644 index 0000000..4ff9a2c --- /dev/null +++ b/patches/4.10/0029-igloo_weaksyms-fix-up-passed-arguments.patch @@ -0,0 +1,28 @@ +From f5ea95cc58d79e1684e9deded48fbb49a388ac4b Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 22 Sep 2025 09:03:56 -0400 +Subject: [PATCH 29/38] igloo_weaksyms: fix up passed arguments + +--- + drivers/igloobase/igloo_weaksyms.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/igloobase/igloo_weaksyms.c b/drivers/igloobase/igloo_weaksyms.c +index 3057659f1a..2fa239ea76 100644 +--- a/drivers/igloobase/igloo_weaksyms.c ++++ b/drivers/igloobase/igloo_weaksyms.c +@@ -75,12 +75,12 @@ void igloo_hc_open(int dfd, struct filename *tmp, int fd) + } + EXPORT_SYMBOL(igloo_hc_open); + +-void (*igloo_ioctl_module)(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp); ++void (*igloo_ioctl_module)(int error, struct file *filp, unsigned int cmd); + void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp); + void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp) + { + if (igloo_ioctl_module) { +- igloo_ioctl_module(error, inode, filp, cmd, argp); ++ igloo_ioctl_module(error, filp, cmd); + } else { + // printk(KERN_EMERG "igloo_ioctl: unimplemented\n"); + } diff --git a/patches/4.10/0030-syscalls_info_report-handle-different-4.10-issues.patch b/patches/4.10/0030-syscalls_info_report-handle-different-4.10-issues.patch new file mode 100644 index 0000000..7757e9c --- /dev/null +++ b/patches/4.10/0030-syscalls_info_report-handle-different-4.10-issues.patch @@ -0,0 +1,30 @@ +From 2a7c6f4c78a884f385d23e21f01f3330d796e131 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 22 Sep 2025 10:57:59 -0400 +Subject: [PATCH 30/38] syscalls_info_report: handle different 4.10 issues + +--- + drivers/igloobase/syscalls_info_report.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/igloobase/syscalls_info_report.c b/drivers/igloobase/syscalls_info_report.c +index 1f447bcc95..917a1041e1 100644 +--- a/drivers/igloobase/syscalls_info_report.c ++++ b/drivers/igloobase/syscalls_info_report.c +@@ -121,9 +121,16 @@ static void report_syscall(char * buffer, struct syscall_metadata *meta){ + #ifdef CONFIG_COMPAT + /* For ARM64 */ + #if defined(CONFIG_ARM64) ++#ifndef syscall_fn_t ++typedef void* syscall_fn_t; ++#endif + extern const syscall_fn_t compat_sys_call_table[]; + /* Don't redeclare sys_call_table as it's already in syscall.h with correct type */ ++#ifdef __NR_compat32_syscalls + #define COMPAT_TABLE_SIZE __NR_compat32_syscalls ++#else ++#define COMPAT_TABLE_SIZE __NR_compat_syscalls ++#endif + + /* For x86_64 */ + // #elif defined(CONFIG_X86_64) diff --git a/patches/4.10/0031-compat.h-formatting-and-forward-declare.patch b/patches/4.10/0031-compat.h-formatting-and-forward-declare.patch new file mode 100644 index 0000000..ff2bf35 --- /dev/null +++ b/patches/4.10/0031-compat.h-formatting-and-forward-declare.patch @@ -0,0 +1,48 @@ +From 9eeec2a5ceb7c677a7096ff1e4f2fd789ec516a7 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 22 Sep 2025 10:58:13 -0400 +Subject: [PATCH 31/38] compat.h: formatting and forward declare + +--- + include/linux/compat.h | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/include/linux/compat.h b/include/linux/compat.h +index 8c02fb57e2..941faf7ac2 100644 +--- a/include/linux/compat.h ++++ b/include/linux/compat.h +@@ -39,6 +39,7 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + #endif + + #define COMPAT_SYSCALL_DEFINE0(name) \ ++ static inline long __do_compat_sys_##name(void); \ + asmlinkage long compat_sys_##name(void) \ + { \ + const char *syscall_basename = __stringify(name); /* Base name */ \ +@@ -47,7 +48,7 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + long skip_ret = 0; \ + /* Array for arguments (empty for 0 args) */ \ + unsigned long args_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ +- ++ \ + /* === Igloo Enter Hook === */ \ + if (igloo_syscall_enter_hook) { \ + /* Pass NULL for setter func for 0-arg syscalls */ \ +@@ -66,7 +67,7 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + } \ + \ + return ret ; \ +- } ++ } \ + static inline long __do_compat_sys_##name(void) + + +@@ -92,7 +93,7 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + void __compat_igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ + { \ + __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); \ +- } ++ } \ + asmlinkage long compat_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));\ + asmlinkage long compat_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))\ + { \ diff --git a/patches/4.10/0032-igloo-debug-calls-now-pr_emerg-when-enabled.patch b/patches/4.10/0032-igloo-debug-calls-now-pr_emerg-when-enabled.patch new file mode 100644 index 0000000..a302286 --- /dev/null +++ b/patches/4.10/0032-igloo-debug-calls-now-pr_emerg-when-enabled.patch @@ -0,0 +1,83 @@ +From adf994aaca52a4043ffe38fa11b38a43bbc23dd3 Mon Sep 17 00:00:00 2001 +From: Zak Estrada +Date: Fri, 24 Oct 2025 16:20:21 -0400 +Subject: [PATCH 32/38] igloo debug calls now pr_emerg when enabled + +--- + drivers/igloobase/igloo_args.c | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/drivers/igloobase/igloo_args.c b/drivers/igloobase/igloo_args.c +index bdb65d1fbc..3adc5b7b67 100644 +--- a/drivers/igloobase/igloo_args.c ++++ b/drivers/igloobase/igloo_args.c +@@ -19,11 +19,11 @@ static int __init early_igloo_task_size(char *p) + { + unsigned long task_size; + if (kstrtoul(p, 0, &task_size) < 0 ) { +- pr_warn("Could not parse igloo_task_size parameter %s\n", p); ++ pr_emerg("Could not parse igloo_task_size parameter %s\n", p); + return -1; + } + igloo_task_size = task_size; +- pr_warn_once("Using igloo_task_size: 0x%lx\n", igloo_task_size); ++ pr_emerg_once("Using igloo_task_size: 0x%lx\n", igloo_task_size); + return 0; + } + early_param("igloo_task_size", early_igloo_task_size); +@@ -35,11 +35,11 @@ static int __init early_igloo_block_halt(char *p) + { + unsigned long block_halt; + if (kstrtoul(p, 0, &block_halt) < 0 ) { +- pr_warn("Could not parse igloo_block_halt parameter %s. Set to 0 (default) or 1\n", p); ++ pr_emerg("Could not parse igloo_block_halt parameter %s. Set to 0 (default) or 1\n", p); + return -1; + } + igloo_block_halt = (block_halt > 0); +- pr_warn_once("Using igloo_block_halt: %d\n", igloo_block_halt); ++ pr_emerg_once("Using igloo_block_halt: %d\n", igloo_block_halt); + return 0; + } + +@@ -75,14 +75,14 @@ static int __init early_igloo_debug_modules(char *p) + // Special case: "all" enables all modules + if (!strcmp(p, "all")) { + memset(&igloo_debug, 1, sizeof(igloo_debug)); +- pr_warn_once("IGLOO: Debug enabled for all modules\n"); ++ pr_emerg_once("IGLOO: Debug enabled for all modules\n"); + return 0; + } + + // Special case: "none" disables all modules (default) + if (!strcmp(p, "none")) { + memset(&igloo_debug, 0, sizeof(igloo_debug)); +- pr_warn_once("IGLOO: Debug disabled for all modules\n"); ++ pr_emerg_once("IGLOO: Debug disabled for all modules\n"); + return 0; + } + +@@ -100,14 +100,14 @@ static int __init early_igloo_debug_modules(char *p) + igloo_debug.osi = true; + else if (!strcmp(token, "all")){ + memset(&igloo_debug, 1, sizeof(igloo_debug)); +- pr_warn_once("IGLOO: Debug enabled for all modules\n"); ++ pr_emerg_once("IGLOO: Debug enabled for all modules\n"); + return 0; + } + else +- pr_warn("IGLOO: Unknown debug module: %s\n", token); ++ pr_emerg("IGLOO: Unknown debug module: %s\n", token); + } + +- pr_warn_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d\n", ++ pr_emerg_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d\n", + igloo_debug.portal, igloo_debug.uprobe, igloo_debug.vma, + igloo_debug.syscall, igloo_debug.osi); + +@@ -115,4 +115,4 @@ static int __init early_igloo_debug_modules(char *p) + } + + early_param("igloo_debug", early_igloo_debug_modules); +-EXPORT_SYMBOL(igloo_debug); +\ No newline at end of file ++EXPORT_SYMBOL(igloo_debug); diff --git a/patches/4.10/0033-open.c-fix-fd-in-igloo_open_hc-29.patch b/patches/4.10/0033-open.c-fix-fd-in-igloo_open_hc-29.patch new file mode 100644 index 0000000..527661a --- /dev/null +++ b/patches/4.10/0033-open.c-fix-fd-in-igloo_open_hc-29.patch @@ -0,0 +1,36 @@ +From aa8b9774e5b75703481d819796a31f27fe3fe28c Mon Sep 17 00:00:00 2001 +From: Benjamin Levy <153011444+be32826@users.noreply.github.com> +Date: Fri, 7 Nov 2025 13:52:38 -0500 +Subject: [PATCH 33/38] open.c: fix fd in igloo_open_hc() (#29) + +--- + fs/open.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/fs/open.c b/fs/open.c +index 309a355ef8..c58f172980 100644 +--- a/fs/open.c ++++ b/fs/open.c +@@ -1053,10 +1053,6 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + +-#ifdef CONFIG_IGLOO +- igloo_hc_open(dfd, tmp, fd); +-#endif +- + fd = get_unused_fd_flags(flags); + if (fd >= 0) { + struct file *f = do_filp_open(dfd, tmp, &op); +@@ -1068,6 +1064,11 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) + fd_install(fd, f); + } + } ++ ++#ifdef CONFIG_IGLOO ++ igloo_hc_open(dfd, tmp, fd); ++#endif ++ + putname(tmp); + return fd; + } diff --git a/patches/4.10/0034-kprobe-options.patch b/patches/4.10/0034-kprobe-options.patch new file mode 100644 index 0000000..80683c5 --- /dev/null +++ b/patches/4.10/0034-kprobe-options.patch @@ -0,0 +1,50 @@ +From da9f5b3d380f3f4784624c8faabd42ed1ac9d1a8 Mon Sep 17 00:00:00 2001 +From: Zak Estrada +Date: Tue, 6 Jan 2026 12:22:54 -0500 +Subject: [PATCH 34/38] kprobe options + +--- + drivers/igloobase/igloo_args.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/igloobase/igloo_args.c b/drivers/igloobase/igloo_args.c +index 3adc5b7b67..3674da3f42 100644 +--- a/drivers/igloobase/igloo_args.c ++++ b/drivers/igloobase/igloo_args.c +@@ -53,6 +53,7 @@ struct igloo_debug_config { + bool vma; // Enable debug for VMA tracking + bool syscall; // Enable debug for syscall tracking + bool osi; // Enable debug for OSI features ++ bool kprobe; // Enable debug for kprobe module + }; + + // Global debug configuration +@@ -62,6 +63,7 @@ struct igloo_debug_config igloo_debug = { + .vma = false, + .syscall = false, + .osi = false, ++ .kprobe = false, + }; + + // Parse comma-separated list of modules to enable debug logging for +@@ -98,6 +100,8 @@ static int __init early_igloo_debug_modules(char *p) + igloo_debug.syscall = true; + else if (!strcmp(token, "osi")) + igloo_debug.osi = true; ++ else if (!strcmp(token, "kprobe")) ++ igloo_debug.kprobe = true; + else if (!strcmp(token, "all")){ + memset(&igloo_debug, 1, sizeof(igloo_debug)); + pr_emerg_once("IGLOO: Debug enabled for all modules\n"); +@@ -107,9 +111,9 @@ static int __init early_igloo_debug_modules(char *p) + pr_emerg("IGLOO: Unknown debug module: %s\n", token); + } + +- pr_emerg_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d\n", ++ pr_emerg_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d kprobe: %d\n", + igloo_debug.portal, igloo_debug.uprobe, igloo_debug.vma, +- igloo_debug.syscall, igloo_debug.osi); ++ igloo_debug.syscall, igloo_debug.osi, igloo_debug.kprobe); + + return 0; + } diff --git a/patches/4.10/0035-igloo-fix-igloo_task_size.patch b/patches/4.10/0035-igloo-fix-igloo_task_size.patch new file mode 100644 index 0000000..1c460fe --- /dev/null +++ b/patches/4.10/0035-igloo-fix-igloo_task_size.patch @@ -0,0 +1,407 @@ +From d19918576cb9bf1a30e0d68f9b7271188cb4c7a3 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 9 May 2026 16:53:37 -0400 +Subject: [PATCH 35/38] igloo: fix igloo_task_size + +--- + arch/arm/mm/mmap.c | 35 ++++++++++++++++++++++++++++------- + arch/arm64/mm/mmap.c | 11 ++++++++++- + arch/mips/mm/mmap.c | 25 ++++++++++++++++++++----- + arch/powerpc/mm/mmap.c | 35 ++++++++++++++++++++++++++++------- + arch/x86/kernel/module.c | 2 ++ + fs/binfmt_elf.c | 18 +++++++++++------- + 6 files changed, 99 insertions(+), 27 deletions(-) + +diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c +index 66353caa35..265bd61527 100644 +--- a/arch/arm/mm/mmap.c ++++ b/arch/arm/mm/mmap.c +@@ -10,6 +10,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + + #define COLOUR_ALIGN(addr,pgoff) \ + ((((addr)+SHMLBA-1)&~(SHMLBA-1)) + \ +@@ -33,13 +36,19 @@ static int mmap_is_legacy(void) + static unsigned long mmap_base(unsigned long rnd) + { + unsigned long gap = rlimit(RLIMIT_STACK); ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + + if (gap < MIN_GAP) + gap = MIN_GAP; + else if (gap > MAX_GAP) + gap = MAX_GAP; + +- return PAGE_ALIGN(TASK_SIZE - gap - rnd); ++ return PAGE_ALIGN(task_size - gap - rnd); + } + + /* +@@ -60,6 +69,12 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, + int do_align = 0; + int aliasing = cache_is_vipt_aliasing(); + struct vm_unmapped_area_info info; ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + + /* + * We only need to do colour alignment if either the I or D +@@ -78,7 +93,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, + return addr; + } + +- if (len > TASK_SIZE) ++ if (len > task_size) + return -ENOMEM; + + if (addr) { +@@ -88,7 +103,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, + addr = PAGE_ALIGN(addr); + + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && ++ if (task_size - len >= addr && + (!vma || addr + len <= vma->vm_start)) + return addr; + } +@@ -96,7 +111,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, + info.flags = 0; + info.length = len; + info.low_limit = mm->mmap_base; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + info.align_mask = do_align ? (PAGE_MASK & (SHMLBA - 1)) : 0; + info.align_offset = pgoff << PAGE_SHIFT; + return vm_unmapped_area(&info); +@@ -113,6 +128,12 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + int do_align = 0; + int aliasing = cache_is_vipt_aliasing(); + struct vm_unmapped_area_info info; ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + + /* + * We only need to do colour alignment if either the I or D +@@ -122,7 +143,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + do_align = filp || (flags & MAP_SHARED); + + /* requested length too big for entire address space */ +- if (len > TASK_SIZE) ++ if (len > task_size) + return -ENOMEM; + + if (flags & MAP_FIXED) { +@@ -139,7 +160,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + else + addr = PAGE_ALIGN(addr); + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && ++ if (task_size - len >= addr && + (!vma || addr + len <= vma->vm_start)) + return addr; + } +@@ -162,7 +183,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + VM_BUG_ON(addr != -ENOMEM); + info.flags = 0; + info.low_limit = mm->mmap_base; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + addr = vm_unmapped_area(&info); + } + +diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c +index 01c171723b..e3451ed3fe 100644 +--- a/arch/arm64/mm/mmap.c ++++ b/arch/arm64/mm/mmap.c +@@ -26,6 +26,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + + #include + +@@ -63,13 +66,19 @@ unsigned long arch_mmap_rnd(void) + static unsigned long mmap_base(unsigned long rnd) + { + unsigned long gap = rlimit(RLIMIT_STACK); ++ unsigned long stack_top = STACK_TOP; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ stack_top = igloo_task_size; ++#endif + + if (gap < MIN_GAP) + gap = MIN_GAP; + else if (gap > MAX_GAP) + gap = MAX_GAP; + +- return PAGE_ALIGN(STACK_TOP - gap - rnd); ++ return PAGE_ALIGN(stack_top - gap - rnd); + } + + /* +diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c +index d08ea3ff0f..c9bd771a86 100644 +--- a/arch/mips/mm/mmap.c ++++ b/arch/mips/mm/mmap.c +@@ -14,6 +14,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + + unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */ + EXPORT_SYMBOL(shm_align_mask); +@@ -36,13 +39,19 @@ static int mmap_is_legacy(void) + static unsigned long mmap_base(unsigned long rnd) + { + unsigned long gap = rlimit(RLIMIT_STACK); ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + + if (gap < MIN_GAP) + gap = MIN_GAP; + else if (gap > MAX_GAP) + gap = MAX_GAP; + +- return PAGE_ALIGN(TASK_SIZE - gap - rnd); ++ return PAGE_ALIGN(task_size - gap - rnd); + } + + #define COLOUR_ALIGN(addr, pgoff) \ +@@ -60,13 +69,19 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + unsigned long addr = addr0; + int do_color_align; + struct vm_unmapped_area_info info; ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + +- if (unlikely(len > TASK_SIZE)) ++ if (unlikely(len > task_size)) + return -ENOMEM; + + if (flags & MAP_FIXED) { + /* Even MAP_FIXED mappings must reside within TASK_SIZE */ +- if (TASK_SIZE - len < addr) ++ if (task_size - len < addr) + return -EINVAL; + + /* +@@ -91,7 +106,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + addr = PAGE_ALIGN(addr); + + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && ++ if (task_size - len >= addr && + (!vma || addr + len <= vma->vm_start)) + return addr; + } +@@ -119,7 +134,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + + info.flags = 0; + info.low_limit = mm->mmap_base; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + return vm_unmapped_area(&info); + } + +diff --git a/arch/powerpc/mm/mmap.c b/arch/powerpc/mm/mmap.c +index 2f1e443621..922de13121 100644 +--- a/arch/powerpc/mm/mmap.c ++++ b/arch/powerpc/mm/mmap.c +@@ -29,6 +29,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + + /* + * Top of mmap area (just below the process stack). +@@ -72,13 +75,19 @@ unsigned long arch_mmap_rnd(void) + static inline unsigned long mmap_base(unsigned long rnd) + { + unsigned long gap = rlimit(RLIMIT_STACK); ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + + if (gap < MIN_GAP) + gap = MIN_GAP; + else if (gap > MAX_GAP) + gap = MAX_GAP; + +- return PAGE_ALIGN(TASK_SIZE - gap - rnd); ++ return PAGE_ALIGN(task_size - gap - rnd); + } + + #ifdef CONFIG_PPC_RADIX_MMU +@@ -95,8 +104,14 @@ radix__arch_get_unmapped_area(struct file *filp, unsigned long addr, + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + struct vm_unmapped_area_info info; ++ unsigned long task_size = TASK_SIZE; + +- if (len > TASK_SIZE - mmap_min_addr) ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif ++ ++ if (len > task_size - mmap_min_addr) + return -ENOMEM; + + if (flags & MAP_FIXED) +@@ -105,7 +120,7 @@ radix__arch_get_unmapped_area(struct file *filp, unsigned long addr, + if (addr) { + addr = PAGE_ALIGN(addr); + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && ++ if (task_size - len >= addr && addr >= mmap_min_addr && + (!vma || addr + len <= vma->vm_start)) + return addr; + } +@@ -113,7 +128,7 @@ radix__arch_get_unmapped_area(struct file *filp, unsigned long addr, + info.flags = 0; + info.length = len; + info.low_limit = mm->mmap_base; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + info.align_mask = 0; + return vm_unmapped_area(&info); + } +@@ -129,9 +144,15 @@ radix__arch_get_unmapped_area_topdown(struct file *filp, + struct mm_struct *mm = current->mm; + unsigned long addr = addr0; + struct vm_unmapped_area_info info; ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + + /* requested length too big for entire address space */ +- if (len > TASK_SIZE - mmap_min_addr) ++ if (len > task_size - mmap_min_addr) + return -ENOMEM; + + if (flags & MAP_FIXED) +@@ -141,7 +162,7 @@ radix__arch_get_unmapped_area_topdown(struct file *filp, + if (addr) { + addr = PAGE_ALIGN(addr); + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && ++ if (task_size - len >= addr && addr >= mmap_min_addr && + (!vma || addr + len <= vma->vm_start)) + return addr; + } +@@ -163,7 +184,7 @@ radix__arch_get_unmapped_area_topdown(struct file *filp, + VM_BUG_ON(addr != -ENOMEM); + info.flags = 0; + info.low_limit = TASK_UNMAPPED_BASE; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + addr = vm_unmapped_area(&info); + } + +diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c +index 477ae806c2..bce156d6e9 100644 +--- a/arch/x86/kernel/module.c ++++ b/arch/x86/kernel/module.c +@@ -199,6 +199,8 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, + } + return 0; + ++invalid_relocation: ++ return -ENOEXEC; + overflow: + pr_err("overflow in relocation type %d val %Lx\n", + (int)ELF64_R_TYPE(rel[i].r_info), val); +diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c +index af32087f08..2911b2670d 100644 +--- a/fs/binfmt_elf.c ++++ b/fs/binfmt_elf.c +@@ -39,7 +39,14 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO + #include ++#define BAD_ADDR(x) ((unsigned long)(x) >= (igloo_task_size ? igloo_task_size : TASK_SIZE)) ++#define IGLOO_TASK_SIZE (igloo_task_size ? igloo_task_size : TASK_SIZE) ++#else ++#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) ++#define IGLOO_TASK_SIZE TASK_SIZE ++#endif + + #ifndef user_long_t + #define user_long_t long +@@ -90,8 +97,6 @@ static struct linux_binfmt elf_format = { + .min_coredump = ELF_EXEC_PAGESIZE, + }; + +-#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) +- + static int set_brk(unsigned long start, unsigned long end) + { + start = ELF_PAGEALIGN(start); +@@ -588,8 +593,8 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, + k = load_addr + eppnt->p_vaddr; + if (BAD_ADDR(k) || + eppnt->p_filesz > eppnt->p_memsz || +- eppnt->p_memsz > TASK_SIZE || +- TASK_SIZE - eppnt->p_memsz < k) { ++ eppnt->p_memsz > IGLOO_TASK_SIZE || ++ IGLOO_TASK_SIZE - eppnt->p_memsz < k) { + error = -ENOMEM; + goto out; + } +@@ -972,9 +977,8 @@ static int load_elf_binary(struct linux_binprm *bprm) + * <= p_memsz so it is only necessary to check p_memsz. + */ + if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz || +- elf_ppnt->p_memsz > TASK_SIZE || +- TASK_SIZE - elf_ppnt->p_memsz < k) { +- /* set_brk can never work. Avoid overflows. */ ++ elf_ppnt->p_memsz > IGLOO_TASK_SIZE || ++ IGLOO_TASK_SIZE - elf_ppnt->p_memsz < k) { /* set_brk can never work. Avoid overflows. */ + retval = -EINVAL; + goto out_free_dentry; + } diff --git a/patches/4.10/0037-igloo-move-into-procfs.patch b/patches/4.10/0037-igloo-move-into-procfs.patch new file mode 100644 index 0000000..c324992 --- /dev/null +++ b/patches/4.10/0037-igloo-move-into-procfs.patch @@ -0,0 +1,194 @@ +From 602218358ca6762cbcb9e0b018ebbc1999b9eb8f Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sun, 17 May 2026 13:21:12 -0400 +Subject: [PATCH 37/38] igloo: move into procfs + +--- + fs/proc/base.c | 143 +++++++++++++++++++++++++++++++++++++++- + include/linux/proc_fs.h | 5 ++ + 2 files changed, 146 insertions(+), 2 deletions(-) + +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 87c9a9aacd..b32679c1a0 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -70,6 +70,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -116,6 +117,125 @@ struct pid_entry { + union proc_op op; + }; + ++#ifdef CONFIG_IGLOO ++struct igloo_proc_pid_entry { ++ struct list_head list; ++ struct proc_dir_entry *pde; ++ const struct file_operations *fop; ++}; ++ ++static LIST_HEAD(igloo_proc_pid_entries); ++static DEFINE_MUTEX(igloo_proc_pid_entries_lock); ++ ++struct proc_dir_entry *igloo_proc_create_pid_data(const char *name, umode_t mode, ++ const struct file_operations *fop, ++ void *data) ++{ ++ struct igloo_proc_pid_entry *entry; ++ struct proc_dir_entry *pde; ++ size_t len; ++ ++ if (!name || !name[0] || strchr(name, '/')) ++ return NULL; ++ if ((mode & S_IFMT) == 0) ++ mode |= S_IFREG; ++ if ((mode & S_IALLUGO) == 0) ++ mode |= S_IRUGO; ++ if (!S_ISREG(mode) || !fop) ++ return NULL; ++ ++ len = strlen(name); ++ if (len >= 256) ++ return NULL; ++ ++ mutex_lock(&igloo_proc_pid_entries_lock); ++ list_for_each_entry(entry, &igloo_proc_pid_entries, list) { ++ pde = entry->pde; ++ if (pde->namelen == len && !memcmp(pde->name, name, len)) { ++ pde->mode = mode; ++ pde->data = data; ++ entry->fop = fop; ++ mutex_unlock(&igloo_proc_pid_entries_lock); ++ return pde; ++ } ++ } ++ mutex_unlock(&igloo_proc_pid_entries_lock); ++ ++ entry = kzalloc(sizeof(*entry), GFP_KERNEL); ++ if (!entry) ++ return NULL; ++ pde = kzalloc(sizeof(*pde) + len + 1, GFP_KERNEL); ++ if (!pde) { ++ kfree(entry); ++ return NULL; ++ } ++ ++ memcpy(pde->name, name, len + 1); ++ pde->namelen = len; ++ pde->mode = mode; ++ pde->nlink = 1; ++ pde->data = data; ++ pde->subdir = RB_ROOT; ++ atomic_set(&pde->count, 1); ++ spin_lock_init(&pde->pde_unload_lock); ++ INIT_LIST_HEAD(&pde->pde_openers); ++ ++ entry->pde = pde; ++ entry->fop = fop; ++ ++ mutex_lock(&igloo_proc_pid_entries_lock); ++ list_add(&entry->list, &igloo_proc_pid_entries); ++ mutex_unlock(&igloo_proc_pid_entries_lock); ++ ++ return pde; ++} ++EXPORT_SYMBOL_GPL(igloo_proc_create_pid_data); ++ ++static int igloo_proc_pid_instantiate(struct inode *dir, struct dentry *dentry, ++ struct task_struct *task, ++ struct igloo_proc_pid_entry *entry) ++{ ++ struct inode *inode; ++ struct proc_dir_entry *pde = entry->pde; ++ ++ inode = proc_pid_make_inode(dir->i_sb, task, pde->mode); ++ if (!inode) ++ return -ENOENT; ++ ++ pde_get(pde); ++ PROC_I(inode)->pde = pde; ++ inode->i_fop = entry->fop; ++ if (pde->size) ++ inode->i_size = pde->size; ++ d_set_d_op(dentry, &pid_dentry_operations); ++ d_add(dentry, inode); ++ if (pid_revalidate(dentry, 0)) ++ return 0; ++ return -ENOENT; ++} ++ ++static int igloo_proc_pid_lookup(struct inode *dir, struct dentry *dentry, ++ struct task_struct *task) ++{ ++ struct igloo_proc_pid_entry *entry; ++ int error = -ENOENT; ++ ++ mutex_lock(&igloo_proc_pid_entries_lock); ++ list_for_each_entry(entry, &igloo_proc_pid_entries, list) { ++ if (entry->pde->namelen != dentry->d_name.len) ++ continue; ++ if (!memcmp(entry->pde->name, dentry->d_name.name, ++ dentry->d_name.len)) { ++ error = igloo_proc_pid_instantiate(dir, dentry, task, ++ entry); ++ break; ++ } ++ } ++ mutex_unlock(&igloo_proc_pid_entries_lock); ++ return error; ++} ++#endif ++ + #define NOD(NAME, MODE, IOP, FOP, OP) { \ + .name = (NAME), \ + .len = sizeof(NAME) - 1, \ +@@ -2956,8 +3076,27 @@ static const struct file_operations proc_tgid_base_operations = { + + static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) + { +- return proc_pident_lookup(dir, dentry, +- tgid_base_stuff, ARRAY_SIZE(tgid_base_stuff)); ++ struct dentry *res; ++#ifdef CONFIG_IGLOO ++ struct task_struct *task; ++ int error; ++#endif ++ ++ res = proc_pident_lookup(dir, dentry, ++ tgid_base_stuff, ARRAY_SIZE(tgid_base_stuff)); ++#ifdef CONFIG_IGLOO ++ if (!IS_ERR(res) || PTR_ERR(res) != -ENOENT) ++ return res; ++ ++ task = get_proc_task(dir); ++ if (!task) ++ return res; ++ error = igloo_proc_pid_lookup(dir, dentry, task); ++ put_task_struct(task); ++ if (!error) ++ return NULL; ++#endif ++ return res; + } + + static const struct inode_operations proc_tgid_base_inode_operations = { +diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h +index 2d2bf592d9..b421adfb99 100644 +--- a/include/linux/proc_fs.h ++++ b/include/linux/proc_fs.h +@@ -27,6 +27,11 @@ extern struct proc_dir_entry *proc_create_data(const char *, umode_t, + struct proc_dir_entry *, + const struct file_operations *, + void *); ++#ifdef CONFIG_IGLOO ++extern struct proc_dir_entry *igloo_proc_create_pid_data(const char *, umode_t, ++ const struct file_operations *, ++ void *); ++#endif + + static inline struct proc_dir_entry *proc_create( + const char *name, umode_t mode, struct proc_dir_entry *parent, diff --git a/patches/4.10/0038-igloo-move-into-signal-handler.patch b/patches/4.10/0038-igloo-move-into-signal-handler.patch new file mode 100644 index 0000000..d73818f --- /dev/null +++ b/patches/4.10/0038-igloo-move-into-signal-handler.patch @@ -0,0 +1,69 @@ +From 68996875436ed6bd835047465f364916d468d394 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sun, 17 May 2026 13:21:20 -0400 +Subject: [PATCH 38/38] igloo: move into signal handler + +--- + include/linux/igloo_signal.h | 12 ++++++++++++ + kernel/signal.c | 15 +++++++++++++++ + 2 files changed, 27 insertions(+) + create mode 100644 include/linux/igloo_signal.h + +diff --git a/include/linux/igloo_signal.h b/include/linux/igloo_signal.h +new file mode 100644 +index 0000000000..375b5ee68b +--- /dev/null ++++ b/include/linux/igloo_signal.h +@@ -0,0 +1,12 @@ ++#ifndef _LINUX_IGLOO_SIGNAL_H ++#define _LINUX_IGLOO_SIGNAL_H ++ ++#include ++ ++struct task_struct; ++ ++typedef bool (*igloo_signal_deliver_hook_t)(int sig, struct task_struct *task); ++ ++extern igloo_signal_deliver_hook_t igloo_signal_deliver_hook; ++ ++#endif /* _LINUX_IGLOO_SIGNAL_H */ +diff --git a/kernel/signal.c b/kernel/signal.c +index 3603d93a19..72976c1210 100644 +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -34,6 +34,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + + #define CREATE_TRACE_POINTS + #include +@@ -51,6 +54,11 @@ + + static struct kmem_cache *sigqueue_cachep; + ++#ifdef CONFIG_IGLOO ++igloo_signal_deliver_hook_t igloo_signal_deliver_hook; ++EXPORT_SYMBOL_GPL(igloo_signal_deliver_hook); ++#endif ++ + int print_fatal_signals __read_mostly; + + static void __user *sig_handler(struct task_struct *t, int sig) +@@ -986,6 +994,13 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, + assert_spin_locked(&t->sighand->siglock); + + result = TRACE_SIGNAL_IGNORED; ++#ifdef CONFIG_IGLOO ++ if (sig && igloo_signal_deliver_hook && ++ igloo_signal_deliver_hook(sig, t)) { ++ ret = 0; ++ goto ret; ++ } ++#endif + if (!prepare_signal(sig, t, + from_ancestor_ns || (info == SEND_SIG_FORCED))) + goto ret; diff --git a/patches/4.10/series b/patches/4.10/series new file mode 100644 index 0000000..05d0ffb --- /dev/null +++ b/patches/4.10/series @@ -0,0 +1,38 @@ +4.10/0001-Net-disallow-changing-bridge-mac-addrs-from-firmadyn.patch +core/add-hypercall.h.patch +4.10/0003-reboot.c-add-igloo_block_halt.patch +4.10/0004-socket.c-add-igloo_socket_hc.patch +core/add-igloo.h.patch +4.10/0006-open.c-add-igloo_open_hc.patch +4.10/0007-sys.c-add-igloo_hc_newuname.patch +4.10/0008-namespace.c-add-igloo_should_block_mount.patch +4.10/0009-ioctl.c-add-igloo_ioctl_hc.patch +4.10/0010-igloobase-add-driver.patch +4.10/0011-exec.c-add-igloo_task_size.patch +4.10/0012-mmap.c-add-igloo_task_size.patch +core/add-igloo_syscall_macros.h.patch +4.10/0014-add-syscalls.h.patch +4.10/0015-syscalls.h-fixes.patch +4.10/0016-syscals.h-fix-sname-in-define0.patch +4.10/0017-binfmt_elf.c-add-igloo_task_size-support.patch +4.10/0018-compat.h-syscalls-support.patch +4.10/0019-give-up-on-gcc-ilog2-constant-optimizations.patch +4.10/0020-extern-yylloc-so-gcc-is-happy.patch +4.10/0021-Fix-build-with-GCC-versions-8-for-some-targets.patch +4.10/0022-igloo_weaksyms-fix-igloo_hc_open-type.patch +4.10/0023-igloo_weaksyms-forward-proper-types-for-4.10.patch +4.10/0024-igloobase.c-4.10-compliance.patch +4.10/0025-osi_notifier-4.10-compliance.patch +4.10/0026-syscalls_info_report-4.10-compliance.patch +4.10/0027-socket.c-add-config_igloo.patch +core/igloo.h-drop-declarations-for-forward-declarations.patch +4.10/0029-igloo_weaksyms-fix-up-passed-arguments.patch +4.10/0030-syscalls_info_report-handle-different-4.10-issues.patch +4.10/0031-compat.h-formatting-and-forward-declare.patch +4.10/0032-igloo-debug-calls-now-pr_emerg-when-enabled.patch +4.10/0033-open.c-fix-fd-in-igloo_open_hc-29.patch +4.10/0034-kprobe-options.patch +4.10/0035-igloo-fix-igloo_task_size.patch +core/update-hypercall.h.patch +4.10/0037-igloo-move-into-procfs.patch +4.10/0038-igloo-move-into-signal-handler.patch diff --git a/patches/6.13/0005-Net-disallow-changing-bridge-mac-addrs-from-firmadyn.patch b/patches/6.13/0005-Net-disallow-changing-bridge-mac-addrs-from-firmadyn.patch new file mode 100644 index 0000000..af161b8 --- /dev/null +++ b/patches/6.13/0005-Net-disallow-changing-bridge-mac-addrs-from-firmadyn.patch @@ -0,0 +1,26 @@ +From bd7d61d4636fb7a5b18d99c3a3bf718bbbf600ff Mon Sep 17 00:00:00 2001 +From: Andrew Fasano +Date: Mon, 18 Dec 2023 09:59:33 -0500 +Subject: [PATCH 05/37] Net: disallow changing bridge mac addrs (from + firmadyne. Unnecessary?) + +--- + net/core/dev.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/core/dev.c b/net/core/dev.c +index a9f62f5aeb..7888f2e4ff 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -9213,6 +9213,11 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa, + const struct net_device_ops *ops = dev->netdev_ops; + int err; + ++ if (dev->priv_flags & IFF_EBRIDGE) { ++ //Changing bridge mac-addrs only causes issues ++ return 0; ++ } ++ + if (!ops->ndo_set_mac_address) + return -EOPNOTSUPP; + if (sa->sa_family != dev->type) diff --git a/patches/6.13/0007-reboot.c-add-igloo_block_halt.patch b/patches/6.13/0007-reboot.c-add-igloo_block_halt.patch new file mode 100644 index 0000000..396db19 --- /dev/null +++ b/patches/6.13/0007-reboot.c-add-igloo_block_halt.patch @@ -0,0 +1,66 @@ +From cb27eddec82f6f126fa9f034eb1139774155cdd6 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 17 Mar 2025 16:02:48 -0400 +Subject: [PATCH 07/37] reboot.c: add igloo_block_halt + +--- + kernel/reboot.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/kernel/reboot.c b/kernel/reboot.c +index a701000bab..463a08b3f9 100644 +--- a/kernel/reboot.c ++++ b/kernel/reboot.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + + /* + * this indicates whether you can reboot with ctrl-alt-del: the default is yes +@@ -281,6 +282,10 @@ static void do_kernel_restart_prepare(void) + */ + void kernel_restart(char *cmd) + { ++ if (igloo_block_halt) { ++ printk("IGLOO: refusing to restart\n"); ++ return; ++ } + kernel_restart_prepare(cmd); + do_kernel_restart_prepare(); + migrate_to_reboot_cpu(); +@@ -309,6 +314,10 @@ static void kernel_shutdown_prepare(enum system_states state) + */ + void kernel_halt(void) + { ++ if (igloo_block_halt) { ++ printk("IGLOO: refusing to halt\n"); ++ return; ++ } + kernel_shutdown_prepare(SYSTEM_HALT); + migrate_to_reboot_cpu(); + syscore_shutdown(); +@@ -699,6 +708,10 @@ EXPORT_SYMBOL_GPL(kernel_can_power_off); + */ + void kernel_power_off(void) + { ++ if (igloo_block_halt) { ++ printk("IGLOO: refusing to power off\n"); ++ return; ++ } + kernel_shutdown_prepare(SYSTEM_POWER_OFF); + do_kernel_power_off_prepare(); + migrate_to_reboot_cpu(); +@@ -726,6 +739,11 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, + char buffer[256]; + int ret = 0; + ++ if (igloo_block_halt) { ++ printk("IGLOO: refusing to reboot\n"); ++ return -EPERM; ++ } ++ + /* We only trust the superuser with rebooting the system. */ + if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) + return -EPERM; diff --git a/patches/6.13/0008-socket.c-add-igloo_socket_hc.patch b/patches/6.13/0008-socket.c-add-igloo_socket_hc.patch new file mode 100644 index 0000000..a2e8303 --- /dev/null +++ b/patches/6.13/0008-socket.c-add-igloo_socket_hc.patch @@ -0,0 +1,52 @@ +From 6570978cf237237b111d0e40c9b4908fcb15b201 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Tue, 29 Jul 2025 13:39:22 -0400 +Subject: [PATCH 08/37] socket.c: add igloo_socket_hc + +--- + net/socket.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/net/socket.c b/net/socket.c +index 9a117248f1..b69372eef4 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -628,10 +628,13 @@ struct socket *sock_alloc(void) + } + EXPORT_SYMBOL(sock_alloc); + ++// forward declare igloo_sock_release ++void igloo_sock_release(struct socket *sock); ++ + static void __sock_release(struct socket *sock, struct inode *inode) + { + const struct proto_ops *ops = READ_ONCE(sock->ops); +- ++ igloo_sock_release(sock); + if (ops) { + struct module *owner = ops->owner; + +@@ -1816,6 +1819,9 @@ SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol, + return __sys_socketpair(family, type, protocol, usockvec); + } + ++// forward declare igloo_sock_bind ++void igloo_sock_bind(struct socket *sock, struct sockaddr_storage *address); ++ + int __sys_bind_socket(struct socket *sock, struct sockaddr_storage *address, + int addrlen) + { +@@ -1823,10 +1829,12 @@ int __sys_bind_socket(struct socket *sock, struct sockaddr_storage *address, + + err = security_socket_bind(sock, (struct sockaddr *)address, + addrlen); +- if (!err) ++ if (!err){ + err = READ_ONCE(sock->ops)->bind(sock, + (struct sockaddr *)address, + addrlen); ++ igloo_sock_bind(sock, address); ++ } + return err; + } + diff --git a/patches/6.13/0010-open.c-add-igloo_open_hc.patch b/patches/6.13/0010-open.c-add-igloo_open_hc.patch new file mode 100644 index 0000000..a321aae --- /dev/null +++ b/patches/6.13/0010-open.c-add-igloo_open_hc.patch @@ -0,0 +1,36 @@ +From 3ac86bbf533d2651f5ac02fa09fccd73596cb77e Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 17 Mar 2025 15:13:19 -0400 +Subject: [PATCH 10/37] open.c: add igloo_open_hc + +--- + fs/open.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/fs/open.c b/fs/open.c +index e6911101fe..ed0828cb91 100644 +--- a/fs/open.c ++++ b/fs/open.c +@@ -1383,6 +1383,11 @@ struct file *file_open_root(const struct path *root, + } + EXPORT_SYMBOL(file_open_root); + ++#ifdef CONFIG_IGLOO ++// forward declare for igloo_hc_open ++void igloo_hc_open(int dfd, struct filename *tmp, int fd); ++#endif ++ + static long do_sys_openat2(int dfd, const char __user *filename, + struct open_how *how) + { +@@ -1396,7 +1401,9 @@ static long do_sys_openat2(int dfd, const char __user *filename, + tmp = getname(filename); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); +- ++ #ifdef CONFIG_IGLOO ++ igloo_hc_open(dfd, tmp, fd); ++ #endif + fd = get_unused_fd_flags(how->flags); + if (fd >= 0) { + struct file *f = do_filp_open(dfd, tmp, &op); diff --git a/patches/6.13/0011-sys.c-add-igloo_hc_newuname.patch b/patches/6.13/0011-sys.c-add-igloo_hc_newuname.patch new file mode 100644 index 0000000..56aa936 --- /dev/null +++ b/patches/6.13/0011-sys.c-add-igloo_hc_newuname.patch @@ -0,0 +1,33 @@ +From c483bc707544112873947e211b355de09d639f37 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Tue, 29 Jul 2025 13:39:56 -0400 +Subject: [PATCH 11/37] sys.c: add igloo_hc_newuname + +--- + kernel/sys.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/kernel/sys.c b/kernel/sys.c +index c4c701c6f0..c472abafbf 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1312,6 +1312,9 @@ static int override_release(char __user *release, size_t len) + return ret; + } + ++// forward declare make_igloo_utsname ++void igloo_hc_newuname(struct new_utsname *name); ++ + SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + { + struct new_utsname tmp; +@@ -1319,6 +1322,9 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) + down_read(&uts_sem); + memcpy(&tmp, utsname(), sizeof(tmp)); + up_read(&uts_sem); ++ ++ igloo_hc_newuname(&tmp); ++ + if (copy_to_user(name, &tmp, sizeof(tmp))) + return -EFAULT; + diff --git a/patches/6.13/0012-namespace.c-add-igloo_should_block_mount.patch b/patches/6.13/0012-namespace.c-add-igloo_should_block_mount.patch new file mode 100644 index 0000000..79d40b5 --- /dev/null +++ b/patches/6.13/0012-namespace.c-add-igloo_should_block_mount.patch @@ -0,0 +1,39 @@ +From b578a0161e836943a169f95655e70bf61bd40db4 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Tue, 29 Jul 2025 13:41:02 -0400 +Subject: [PATCH 12/37] namespace.c: add igloo_should_block_mount + +--- + fs/namespace.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/fs/namespace.c b/fs/namespace.c +index eac057e569..921c99f2c7 100644 +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -3746,6 +3746,11 @@ static char *copy_mount_string(const void __user *data) + return data ? strndup_user(data, PATH_MAX) : NULL; + } + ++#ifdef CONFIG_IGLOO ++// forward declare igloo_should_block_mount ++bool igloo_should_block_mount(struct path *path); ++#endif ++ + /* + * Flags is a 32-bit value that allows up to 31 non-fs dependent flags to + * be given to the mount() call (ie: read-only, no-dev, no-suid etc). +@@ -3784,7 +3789,12 @@ int path_mount(const char *dev_name, struct path *path, + return -EPERM; + if (flags & SB_MANDLOCK) + warn_mandlock(); +- ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_should_block_mount(path)){ ++ return 0; ++ } ++#endif + /* Default to relatime unless overriden */ + if (!(flags & MS_NOATIME)) + mnt_flags |= MNT_RELATIME; diff --git a/patches/6.13/0013-ioctl.c-add-igloo_ioctl_hc.patch b/patches/6.13/0013-ioctl.c-add-igloo_ioctl_hc.patch new file mode 100644 index 0000000..2b5423b --- /dev/null +++ b/patches/6.13/0013-ioctl.c-add-igloo_ioctl_hc.patch @@ -0,0 +1,52 @@ +From eb2e953c1def35bb92deeec5763667ddbc9e31ec Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 17 Mar 2025 15:12:52 -0400 +Subject: [PATCH 13/37] ioctl.c: add igloo_ioctl_hc + +--- + fs/ioctl.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/fs/ioctl.c b/fs/ioctl.c +index 638a36be31..270ad13437 100644 +--- a/fs/ioctl.c ++++ b/fs/ioctl.c +@@ -789,6 +789,11 @@ static int ioctl_get_fs_sysfs_path(struct file *file, void __user *argp) + return copy_to_user(argp, &u, sizeof(u)) ? -EFAULT : 0; + } + ++#ifdef CONFIG_IGLOO ++// forward declare igloo_ioctl ++void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user * argp); ++#endif ++ + /* + * do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. + * It's just a simple helper for sys_ioctl and compat_sys_ioctl. +@@ -799,8 +804,8 @@ static int ioctl_get_fs_sysfs_path(struct file *file, void __user *argp) + * The LSM mailing list should also be notified of any command additions or + * changes, as specific LSMs may be affected. + */ +-static int do_vfs_ioctl(struct file *filp, unsigned int fd, +- unsigned int cmd, unsigned long arg) ++static int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, ++ unsigned long arg) + { + void __user *argp = (void __user *)arg; + struct inode *inode = file_inode(filp); +@@ -881,8 +886,13 @@ static int do_vfs_ioctl(struct file *filp, unsigned int fd, + return ioctl_get_fs_sysfs_path(filp, argp); + + default: +- if (S_ISREG(inode->i_mode)) +- return file_ioctl(filp, cmd, argp); ++ if (S_ISREG(inode->i_mode)){ ++ int error = file_ioctl(filp, cmd, argp); ++ #ifdef CONFIG_IGLOO ++ igloo_ioctl(error, inode, filp, cmd, argp); ++ #endif ++ return error; ++ } + break; + } + diff --git a/patches/6.13/0014-igloobase-add-driver.patch b/patches/6.13/0014-igloobase-add-driver.patch new file mode 100644 index 0000000..e038eb6 --- /dev/null +++ b/patches/6.13/0014-igloobase-add-driver.patch @@ -0,0 +1,723 @@ +From 9aaca4d6a58568055ae73955430756b9a70fc55f Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:10:02 -0400 +Subject: [PATCH 14/37] igloobase: add driver + +--- + drivers/Kconfig | 2 + + drivers/Makefile | 3 + + drivers/igloobase/Kconfig | 5 + + drivers/igloobase/Makefile | 2 + + drivers/igloobase/igloo_args.c | 118 ++++++++++ + drivers/igloobase/igloo_weaksyms.c | 112 +++++++++ + drivers/igloobase/igloobase.c | 28 +++ + drivers/igloobase/igloobase.h | 2 + + drivers/igloobase/igloobasehypercalls.h | 6 + + drivers/igloobase/osi_notifier.c | 55 +++++ + drivers/igloobase/syscalls_info_report.c | 281 +++++++++++++++++++++++ + 11 files changed, 614 insertions(+) + create mode 100644 drivers/igloobase/Kconfig + create mode 100644 drivers/igloobase/Makefile + create mode 100644 drivers/igloobase/igloo_args.c + create mode 100644 drivers/igloobase/igloo_weaksyms.c + create mode 100644 drivers/igloobase/igloobase.c + create mode 100644 drivers/igloobase/igloobase.h + create mode 100644 drivers/igloobase/igloobasehypercalls.h + create mode 100644 drivers/igloobase/osi_notifier.c + create mode 100644 drivers/igloobase/syscalls_info_report.c + +diff --git a/drivers/Kconfig b/drivers/Kconfig +index 7bdad836fc..f08fe9f627 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -245,4 +245,6 @@ source "drivers/cdx/Kconfig" + + source "drivers/dpll/Kconfig" + ++source "drivers/igloobase/Kconfig" ++ + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index 45d1c3e630..02d636d913 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -195,3 +195,6 @@ obj-$(CONFIG_CDX_BUS) += cdx/ + obj-$(CONFIG_DPLL) += dpll/ + + obj-$(CONFIG_S390) += s390/ ++ ++# Added ++obj-y += igloobase/ +\ No newline at end of file +diff --git a/drivers/igloobase/Kconfig b/drivers/igloobase/Kconfig +new file mode 100644 +index 0000000000..b20c5df509 +--- /dev/null ++++ b/drivers/igloobase/Kconfig +@@ -0,0 +1,5 @@ ++config IGLOO ++ bool "IGLOO module support" ++ default y ++ help ++ Support for IGLOO analysis +\ No newline at end of file +diff --git a/drivers/igloobase/Makefile b/drivers/igloobase/Makefile +new file mode 100644 +index 0000000000..1865aadfbb +--- /dev/null ++++ b/drivers/igloobase/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_IGLOO) += osi_notifier.o syscalls_info_report.o \ ++ igloobase.o igloo_weaksyms.o igloo_args.o +\ No newline at end of file +diff --git a/drivers/igloobase/igloo_args.c b/drivers/igloobase/igloo_args.c +new file mode 100644 +index 0000000000..bdb65d1fbc +--- /dev/null ++++ b/drivers/igloobase/igloo_args.c +@@ -0,0 +1,118 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "igloo_syscall_macros.h" ++#include "igloo.h" ++ ++ ++/** ++ * Early params originally from igloo_hc.c in the module ++ */ ++unsigned long igloo_task_size = 0; ++static int __init early_igloo_task_size(char *p) ++{ ++ unsigned long task_size; ++ if (kstrtoul(p, 0, &task_size) < 0 ) { ++ pr_warn("Could not parse igloo_task_size parameter %s\n", p); ++ return -1; ++ } ++ igloo_task_size = task_size; ++ pr_warn_once("Using igloo_task_size: 0x%lx\n", igloo_task_size); ++ return 0; ++} ++early_param("igloo_task_size", early_igloo_task_size); ++EXPORT_SYMBOL(igloo_task_size); ++ ++bool igloo_block_halt=false; ++ ++static int __init early_igloo_block_halt(char *p) ++{ ++ unsigned long block_halt; ++ if (kstrtoul(p, 0, &block_halt) < 0 ) { ++ pr_warn("Could not parse igloo_block_halt parameter %s. Set to 0 (default) or 1\n", p); ++ return -1; ++ } ++ igloo_block_halt = (block_halt > 0); ++ pr_warn_once("Using igloo_block_halt: %d\n", igloo_block_halt); ++ return 0; ++} ++ ++early_param("igloo_block_halt", early_igloo_block_halt); ++EXPORT_SYMBOL(igloo_block_halt); ++ ++// Debug logging configuration for each module ++struct igloo_debug_config { ++ bool portal; // Enable debug for portal module ++ bool uprobe; // Enable debug for uprobe module ++ bool vma; // Enable debug for VMA tracking ++ bool syscall; // Enable debug for syscall tracking ++ bool osi; // Enable debug for OSI features ++}; ++ ++// Global debug configuration ++struct igloo_debug_config igloo_debug = { ++ .portal = false, ++ .uprobe = false, ++ .vma = false, ++ .syscall = false, ++ .osi = false, ++}; ++ ++// Parse comma-separated list of modules to enable debug logging for ++static int __init early_igloo_debug_modules(char *p) ++{ ++ char *token; ++ ++ // By default, all modules have debug disabled ++ memset(&igloo_debug, 0, sizeof(igloo_debug)); ++ ++ // Special case: "all" enables all modules ++ if (!strcmp(p, "all")) { ++ memset(&igloo_debug, 1, sizeof(igloo_debug)); ++ pr_warn_once("IGLOO: Debug enabled for all modules\n"); ++ return 0; ++ } ++ ++ // Special case: "none" disables all modules (default) ++ if (!strcmp(p, "none")) { ++ memset(&igloo_debug, 0, sizeof(igloo_debug)); ++ pr_warn_once("IGLOO: Debug disabled for all modules\n"); ++ return 0; ++ } ++ ++ // Parse comma-separated module list ++ while ((token = strsep(&p, ",")) != NULL) { ++ if (!strcmp(token, "portal")) ++ igloo_debug.portal = true; ++ else if (!strcmp(token, "uprobe")) ++ igloo_debug.uprobe = true; ++ else if (!strcmp(token, "vma")) ++ igloo_debug.vma = true; ++ else if (!strcmp(token, "syscall")) ++ igloo_debug.syscall = true; ++ else if (!strcmp(token, "osi")) ++ igloo_debug.osi = true; ++ else if (!strcmp(token, "all")){ ++ memset(&igloo_debug, 1, sizeof(igloo_debug)); ++ pr_warn_once("IGLOO: Debug enabled for all modules\n"); ++ return 0; ++ } ++ else ++ pr_warn("IGLOO: Unknown debug module: %s\n", token); ++ } ++ ++ pr_warn_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d\n", ++ igloo_debug.portal, igloo_debug.uprobe, igloo_debug.vma, ++ igloo_debug.syscall, igloo_debug.osi); ++ ++ return 0; ++} ++ ++early_param("igloo_debug", early_igloo_debug_modules); ++EXPORT_SYMBOL(igloo_debug); +\ No newline at end of file +diff --git a/drivers/igloobase/igloo_weaksyms.c b/drivers/igloobase/igloo_weaksyms.c +new file mode 100644 +index 0000000000..8498d15500 +--- /dev/null ++++ b/drivers/igloobase/igloo_weaksyms.c +@@ -0,0 +1,112 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "igloo_syscall_macros.h" ++#include "igloo.h" ++ ++/* Syscall hooks */ ++igloo_syscall_enter_t igloo_syscall_enter_hook = NULL; ++igloo_syscall_return_t igloo_syscall_return_hook = NULL; ++EXPORT_SYMBOL(igloo_syscall_enter_hook); ++EXPORT_SYMBOL(igloo_syscall_return_hook); ++ ++// Function pointer for override ++bool (*igloo_should_block_mount_module)(struct path *path); ++bool igloo_should_block_mount(struct path *path); ++bool igloo_should_block_mount(struct path *path) ++{ ++ if (igloo_should_block_mount_module) { ++ return igloo_should_block_mount_module(path); ++ } else { ++ // printk(KERN_INFO "igloo_should_block_mount: default implementation called\n"); ++ return false; ++ } ++} ++EXPORT_SYMBOL(igloo_should_block_mount); ++ ++void (*igloo_sock_release_module)(struct socket *sock); ++void igloo_sock_release(struct socket *sock) ++{ ++ if (igloo_sock_release_module) { ++ igloo_sock_release_module(sock); ++ } else { ++ // printk(KERN_EMERG "igloo_sock_release: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_sock_release); ++ ++void (*igloo_sock_bind_module)(struct socket *sock, struct sockaddr_storage *address); ++void igloo_sock_bind(struct socket *sock, struct sockaddr_storage *address) ++{ ++ if (igloo_sock_bind_module) { ++ igloo_sock_bind_module(sock, address); ++ } else { ++ // printk(KERN_EMERG "igloo_sock_bind: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_sock_bind); ++ ++void (*igloo_hc_newuname_module)(struct new_utsname *name) = NULL; ++void igloo_hc_newuname(struct new_utsname *name) ++{ ++ if (igloo_hc_newuname_module) { ++ igloo_hc_newuname_module(name); ++ } else { ++ // printk(KERN_EMERG "igloo_hc_newuname: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_hc_newuname); ++ ++void (*igloo_hc_open_module)(int dfd, const char __user *filename, struct open_how *how); ++void igloo_hc_open(int dfd, const char __user *filename, struct open_how *how); ++void igloo_hc_open(int dfd, const char __user *filename, struct open_how *how) ++{ ++ if (igloo_hc_open_module) { ++ igloo_hc_open_module(dfd, filename, how); ++ } else { ++ // printk(KERN_EMERG "igloo_hc_open: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_hc_open); ++ ++void (*igloo_ioctl_module)(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp); ++void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp); ++void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp) ++{ ++ if (igloo_ioctl_module) { ++ igloo_ioctl_module(error, inode, filp, cmd, argp); ++ } else { ++ // printk(KERN_EMERG "igloo_ioctl: unimplemented\n"); ++ } ++} ++EXPORT_SYMBOL(igloo_ioctl); ++ ++/* Export internal symbols needed for introspection research */ ++ ++// Symbol lookup functions - now pulled in by trace/syscall.h ++EXPORT_SYMBOL(kallsyms_lookup); ++ ++extern unsigned long kallsyms_lookup_name(const char *name); ++EXPORT_SYMBOL(kallsyms_lookup_name); ++ ++// Architecture-specific functions ++extern const char *arch_vma_name(struct vm_area_struct *vma); ++EXPORT_SYMBOL(arch_vma_name); ++ ++// Process management ++extern pid_t kernel_clone(struct kernel_clone_args *args); ++EXPORT_SYMBOL(kernel_clone); ++ ++extern int kill_pid_info(int sig, struct kernel_siginfo *info, struct pid *pid); ++EXPORT_SYMBOL(kill_pid_info); ++ ++// Memory access ++extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, ++ void *buf, int len, unsigned int gup_flags); ++EXPORT_SYMBOL(access_remote_vm); +\ No newline at end of file +diff --git a/drivers/igloobase/igloobase.c b/drivers/igloobase/igloobase.c +new file mode 100644 +index 0000000000..005835633c +--- /dev/null ++++ b/drivers/igloobase/igloobase.c +@@ -0,0 +1,28 @@ ++#include ++#include ++#include ++#include "igloo.h" ++#include "igloobase.h" ++ ++ ++/* Register probes for mmap and munmap */ ++static int __init igloo_base_init(void) { ++ printk(KERN_EMERG "IGLOOBase: Initializing\n"); ++ int ret = 0; ++ if ((ret = osi_notifier_init()) != 0) { ++ printk(KERN_ERR "Failed to register osi_notifier_init\n"); ++ } ++ if ((ret = syscalls_info_report()) != 0) { ++ printk(KERN_ERR "Failed to register syscalls_hc returning %d\n", ret); ++ } ++ return 0; ++} ++ ++/* Unregister probes */ ++static void __exit igloo_base_exit(void) { ++ // Unreachable, module is built in ++ printk(KERN_ERR "TODO\n"); ++} ++ ++module_init(igloo_base_init); ++module_exit(igloo_base_exit); +\ No newline at end of file +diff --git a/drivers/igloobase/igloobase.h b/drivers/igloobase/igloobase.h +new file mode 100644 +index 0000000000..3fe72b89a0 +--- /dev/null ++++ b/drivers/igloobase/igloobase.h +@@ -0,0 +1,2 @@ ++int osi_notifier_init(void); ++int syscalls_info_report(void); +\ No newline at end of file +diff --git a/drivers/igloobase/igloobasehypercalls.h b/drivers/igloobase/igloobasehypercalls.h +new file mode 100644 +index 0000000000..f7fa8b985e +--- /dev/null ++++ b/drivers/igloobase/igloobasehypercalls.h +@@ -0,0 +1,6 @@ ++// This should be a relatively small file as most functionality should be ++// implemented in the core igloo driver ++ ++enum igloo_base_hypercalls { ++ IGLOO_HYP_SETUP_SYSCALL = 0x1337, ++}; +\ No newline at end of file +diff --git a/drivers/igloobase/osi_notifier.c b/drivers/igloobase/osi_notifier.c +new file mode 100644 +index 0000000000..b9ed55d799 +--- /dev/null ++++ b/drivers/igloobase/osi_notifier.c +@@ -0,0 +1,55 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "hypercall.h" ++#include ++#include ++#include ++#include ++#include /* Needed by all modules */ ++#include /* Needed for KERN_INFO */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "igloo.h" ++#include "igloobase.h" ++ ++#define IGLOO_HYP_OSI_TASK_SWITCH 0x3337 ++ ++// Define a tracepoint probe function for sched_switch with the correct signature ++static void probe_sched_switch(void *data, bool preempt, struct task_struct *prev, ++ struct task_struct *next, unsigned int prev_state) ++{ ++ // Notify hypervisor about task switch using task pointers ++ igloo_hypercall2(IGLOO_HYP_OSI_TASK_SWITCH, (unsigned long)prev, (unsigned long)next); ++} ++ ++int osi_notifier_init(void) { ++ int ret = 0; ++ ++ // Register the sched_switch tracepoint ++ ret = register_trace_sched_switch(probe_sched_switch, NULL); ++ if (ret) { ++ printk(KERN_ERR "IGLOO: Failed to register sched_switch tracepoint, returned %d\n", ret); ++ } else { ++ printk(KERN_INFO "IGLOO: Successfully registered sched_switch tracepoint\n"); ++ } ++ return 0; ++} +\ No newline at end of file +diff --git a/drivers/igloobase/syscalls_info_report.c b/drivers/igloobase/syscalls_info_report.c +new file mode 100644 +index 0000000000..9927c02e3e +--- /dev/null ++++ b/drivers/igloobase/syscalls_info_report.c +@@ -0,0 +1,281 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "hypercall.h" // Content is now included directly below ++#include "igloo.h" ++#include "igloobase.h" ++#include "igloobasehypercalls.h" ++ ++extern struct syscall_metadata *__start_syscalls_metadata[]; ++extern struct syscall_metadata *__stop_syscalls_metadata[]; ++ ++#ifndef ARCH_HAS_SYSCALL_MATCH_SYM_NAME ++static inline bool arch_syscall_match_sym_name(const char *sym, const char *name) ++{ ++ /* ++ * Only compare after the "sys" prefix. Archs that use ++ * syscall wrappers may have syscalls symbols aliases prefixed ++ * with ".SyS" or ".sys" instead of "sys", leading to an unwanted ++ * mismatch. ++ */ ++ return !strcmp(sym + 3, name + 3); ++} ++#endif ++ ++/* Normalize syscall names by removing common prefixes like 'sys_', '_sys_', 'compat_sys_' */ ++static inline const char *normalize_syscall_name(const char *name) ++{ ++ if (!name) ++ return NULL; ++ ++ /* Skip leading underscores (e.g. _sys_) */ ++ while (*name == '_') ++ name++; ++ ++ /* Check for 'sys_' prefix */ ++ if (strncmp(name, "sys_", 4) == 0) ++ return name + 4; ++ ++ /* Check for 'compat_sys_' prefix */ ++ if (strncmp(name, "compat_sys_", 11) == 0) ++ return name + 11; ++ ++ /* Check for other arch-specific prefixes */ ++ if (strncmp(name, "arm64_sys_", 10) == 0) ++ return name + 10; ++ ++ if (strncmp(name, "riscv_sys_", 10) == 0) ++ return name + 10; ++ ++ return name; ++} ++ ++// copied from trace_syscalls.c ++static struct syscall_metadata * ++find_syscall_meta_copy(unsigned long syscall); ++static struct syscall_metadata * ++find_syscall_meta_copy(unsigned long syscall) ++{ ++ struct syscall_metadata **start; ++ struct syscall_metadata **stop; ++ char str[KSYM_SYMBOL_LEN]; ++ ++ ++ start = __start_syscalls_metadata; ++ stop = __stop_syscalls_metadata; ++ kallsyms_lookup(syscall, NULL, NULL, NULL, str); ++ ++ if (arch_syscall_match_sym_name(str, "sys_ni_syscall")) ++ return NULL; ++ ++ for ( ; start < stop; start++) { ++ if ((*start)->name && arch_syscall_match_sym_name(str, (*start)->name)) ++ return *start; ++ } ++ return NULL; ++} ++ ++static void report_syscall(char * buffer, struct syscall_metadata *meta){ ++ if (!meta || !meta->name) { ++ return; // Skip invalid metadata ++ } ++ // Prepare JSON metadata for hypercall (ensure buffer is large enough) ++ int x = snprintf(buffer, PAGE_SIZE, ++ "{\"name\": \"%s\", \"args\":[", ++ normalize_syscall_name(meta->name)); ++ ++ for (int j = 0; j < meta->nb_args && x > 0 && x < PAGE_SIZE; j++) { ++ // Append args safely, checking remaining buffer space ++ x += snprintf((char*)buffer + x, PAGE_SIZE - x, "[\"%s\", \"%s\"]%s", ++ meta->types[j] ? meta->types[j] : "?", // Handle potential NULL type/arg names ++ meta->args[j] ? meta->args[j] : "?", ++ j + 1 < meta->nb_args ? ", " : ""); ++ } ++ ++ if (x > 0 && x < PAGE_SIZE) { ++ x += snprintf((char*)buffer + x, PAGE_SIZE - x, "]}"); ++ } ++ ++ if (x <= 0 || x >= PAGE_SIZE) { ++ // DBG_PRINTK( "IGLOO: Failed to format JSON for syscall %s (nr %d) - buffer overflow or snprintf error.\n", meta->name, meta->syscall_nr); ++ // Decide how to handle: skip this probe or abort? Skipping for now. ++ return; ++ } ++ // Send metadata via hypercall (call returns value, but it's ignored here) ++ igloo_hypercall(IGLOO_HYP_SETUP_SYSCALL, (unsigned long)buffer); ++} ++ ++// normalize_syscall_name is now defined in syscalls_hc.h ++ ++#ifdef CONFIG_COMPAT ++/* For ARM64 */ ++#if defined(CONFIG_ARM64) ++extern const syscall_fn_t compat_sys_call_table[]; ++/* Don't redeclare sys_call_table as it's already in syscall.h with correct type */ ++#define COMPAT_TABLE_SIZE __NR_compat32_syscalls ++ ++/* For x86_64 */ ++// #elif defined(CONFIG_X86_64) ++// /* Use void* instead of syscall_fn_t for broader compatibility */ ++// extern const void * const ia32_sys_call_table[]; ++// #define compat_sys_call_table ia32_sys_call_table ++// #define COMPAT_TABLE_SIZE IA32_NR_syscalls ++ ++/* For MIPS64 */ ++#elif defined(CONFIG_MIPS) && defined(CONFIG_64BIT) ++/* Use the correct declaration that matches what's in syscall.h */ ++#include /* Ensure we get the right declaration */ ++#define compat_sys_call_table sys32_call_table ++#define COMPAT_TABLE_SIZE NR_syscalls /* Use NR_syscalls instead of __NR_syscalls */ ++ ++/* For PPC64 */ ++// #elif defined(CONFIG_PPC64) ++// extern void *sys32_call_table[]; ++// #define compat_sys_call_table sys32_call_table ++// #define COMPAT_TABLE_SIZE __NR_syscalls ++ ++/* For RISC-V64 */ ++#elif defined(CONFIG_RISCV) && defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) ++/* Use the correct declaration for RISC-V - it's already properly declared in syscall.h */ ++#define COMPAT_TABLE_SIZE __NR_syscalls ++#endif ++#endif ++ ++/* Get syscall name from a function pointer */ ++#ifdef CONFIG_COMPAT ++static const char *get_syscall_name_from_func(void *func_ptr) { ++ char sym[KSYM_SYMBOL_LEN]; ++ ++ if (!func_ptr || IS_ERR(func_ptr)) ++ return NULL; ++ ++ kallsyms_lookup((unsigned long)func_ptr, NULL, NULL, NULL, sym); ++ ++ /* Skip if we couldn't identify the symbol */ ++ if (!sym[0]) ++ return NULL; ++ ++ /* Skip the "sys_" or similar prefix */ ++ if (strncmp(sym, "sys_", 4) == 0) ++ return kstrdup(sym, GFP_KERNEL); ++ else if (strncmp(sym, "compat_sys_", 11) == 0) ++ return kstrdup(sym + 7, GFP_KERNEL); /* Return without the "compat_" prefix */ ++ else if (strncmp(sym, "__arm64_", 8) == 0) ++ return kstrdup(sym + 8, GFP_KERNEL); /* Return without the "__arm64_" prefix */ ++ else if (strncmp(sym, "__loongarch_", 12) == 0) ++ return kstrdup(sym + 12, GFP_KERNEL); /* Return without the "__loongarch_" prefix */ ++ else if (strncmp(sym, "__riscv_", 8) == 0) ++ return kstrdup(sym + 8, GFP_KERNEL); /* Return without the "__riscv_" prefix */ ++ else if (strncmp(sym, "__se_", 5) == 0) ++ return kstrdup(sym + 5, GFP_KERNEL); /* Return without the "__se_" prefix */ ++ ++ return kstrdup(sym, GFP_KERNEL); ++} ++ ++static void report_syscall_from_func(char *buffer, void *func_ptr, int syscall_nr) { ++ const char *name; ++ int x; ++ ++ if (!func_ptr || IS_ERR(func_ptr)) ++ return; ++ ++ name = get_syscall_name_from_func(func_ptr); ++ if (!name) ++ return; ++ ++ /* Create a simplified metadata report for compat syscalls */ ++ x = snprintf(buffer, PAGE_SIZE, "{\"name\": \"%s\", \"compat\": true, \"args\":\"unknown\"}", ++ normalize_syscall_name(name)); ++ ++ if (x > 0 && x < PAGE_SIZE) { ++ /* Send this metadata via hypercall */ ++ igloo_hypercall(IGLOO_HYP_SETUP_SYSCALL, (unsigned long)buffer); ++ } ++ ++ kfree(name); ++} ++#endif ++ ++int syscalls_info_report(void) { ++ printk(KERN_EMERG "IGLOO: Initializing syscall hypercalls\n"); ++ struct syscall_metadata **p = __start_syscalls_metadata; ++ struct syscall_metadata **end = __stop_syscalls_metadata; ++ ++ void *buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); ++ ++ if (!buffer) { ++ printk(KERN_ERR "IGLOO: Failed to allocate memory for syscall metadata buffer\n"); ++ return -ENOMEM; ++ } ++ ++ // Count the number of syscalls ++ int num_syscall_probes = end - p; ++ if (num_syscall_probes <= 0) { ++ printk(KERN_WARNING "IGLOO: No syscall metadata found.\n"); ++ return -EINVAL; ++ } ++ ++ // Process regular syscalls first ++ int i; ++ for (i = 0; i < NR_syscalls+1000; i++) { ++ struct syscall_metadata *meta; ++ unsigned long addr; ++ addr = arch_syscall_addr(i); ++ meta = find_syscall_meta_copy(addr); ++ if (!meta) ++ continue; ++ meta->syscall_nr = i; ++ report_syscall(buffer, meta); ++ } ++ ++ for (p = __start_syscalls_metadata; p < end; p++) { ++ struct syscall_metadata *meta = *p; ++ if (!meta) { ++ continue; // Skip invalid metadata ++ } ++ report_syscall(buffer, meta); ++ } ++ ++ // Process compat syscalls ++#ifdef CONFIG_COMPAT ++#ifdef COMPAT_TABLE_SIZE ++ printk(KERN_INFO "IGLOO: Processing compat syscall table with %d entries\n", COMPAT_TABLE_SIZE); ++ ++ for (i = 0; i < COMPAT_TABLE_SIZE; i++) { ++#if defined(CONFIG_RISCV) && defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) ++ /* For RISC-V, use the already declared variable without casting */ ++ void *func_ptr = compat_sys_call_table[i]; ++#elif defined(CONFIG_MIPS) && defined(CONFIG_64BIT) ++ /* For MIPS64, handle the unsigned long array correctly */ ++ void *func_ptr = (void *)(unsigned long)compat_sys_call_table[i]; ++#else ++ /* For other architectures, use proper casting based on architecture pointer size */ ++ void *func_ptr = (void *)(uintptr_t)compat_sys_call_table[i]; ++#endif ++ ++ /* Skip non-existent syscalls (usually NULL) */ ++ if (!func_ptr || IS_ERR(func_ptr)) { ++ continue; ++ } ++ ++ report_syscall_from_func(buffer, func_ptr, i); ++ } ++#else ++ printk(KERN_INFO "IGLOO: No compat syscall table found for this architecture\n"); ++#endif ++#endif ++ ++ kfree(buffer); ++ return 0; ++} +\ No newline at end of file diff --git a/patches/6.13/0015-exec.c-add-igloo_task_size.patch b/patches/6.13/0015-exec.c-add-igloo_task_size.patch new file mode 100644 index 0000000..31511ca --- /dev/null +++ b/patches/6.13/0015-exec.c-add-igloo_task_size.patch @@ -0,0 +1,32 @@ +From ca6ce59643e2995add53445a7f05ee8ee117b017 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:15:02 -0400 +Subject: [PATCH 15/37] exec.c: add igloo_task_size + +--- + fs/exec.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/fs/exec.c b/fs/exec.c +index 98cb7ba998..db46361140 100644 +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -72,6 +72,7 @@ + #include + #include + #include ++#include + + #include + #include "internal.h" +@@ -1432,6 +1433,10 @@ void setup_new_exec(struct linux_binprm * bprm) + * some architectures like powerpc + */ + me->mm->task_size = TASK_SIZE; ++ #ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ current->mm->task_size = igloo_task_size; ++ #endif + up_write(&me->signal->exec_update_lock); + mutex_unlock(&me->signal->cred_guard_mutex); + } diff --git a/patches/6.13/0016-mmap.c-add-igloo_task_size.patch b/patches/6.13/0016-mmap.c-add-igloo_task_size.patch new file mode 100644 index 0000000..6698701 --- /dev/null +++ b/patches/6.13/0016-mmap.c-add-igloo_task_size.patch @@ -0,0 +1,36 @@ +From 6d3cbdd1be758a91954340c66f6f3647bf23f9be Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:16:06 -0400 +Subject: [PATCH 16/37] mmap.c: add igloo_task_size + +--- + arch/x86/mm/mmap.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c +index b8a6ffffb4..d8a767a046 100644 +--- a/arch/x86/mm/mmap.c ++++ b/arch/x86/mm/mmap.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #include "physaddr.h" + +@@ -102,7 +103,13 @@ static unsigned long mmap_base(unsigned long rnd, unsigned long task_size, + else if (gap > gap_max) + gap = gap_max; + +- return PAGE_ALIGN(task_size - gap - rnd); ++ #ifdef CONFIG_IGLOO ++ if(igloo_task_size) { ++ return PAGE_ALIGN(igloo_task_size - gap - rnd); ++ } else { ++ return PAGE_ALIGN(TASK_SIZE - gap - rnd); ++ } ++ #endif + } + + static unsigned long mmap_legacy_base(unsigned long rnd, diff --git a/patches/6.13/0017-util.c-add-igloo_task_size.patch b/patches/6.13/0017-util.c-add-igloo_task_size.patch new file mode 100644 index 0000000..852348d --- /dev/null +++ b/patches/6.13/0017-util.c-add-igloo_task_size.patch @@ -0,0 +1,36 @@ +From 55f001f78b3a7febc729457685856a4b168ab626 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:16:19 -0400 +Subject: [PATCH 17/37] util.c: add igloo_task_size + +--- + mm/util.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/mm/util.c b/mm/util.c +index 60aa40f612..f5d5f5dac3 100644 +--- a/mm/util.c ++++ b/mm/util.c +@@ -25,6 +25,7 @@ + #include + + #include ++#include + + #include + +@@ -437,7 +438,13 @@ static unsigned long mmap_base(unsigned long rnd, struct rlimit *rlim_stack) + * task. mmap_base starts directly below the stack and grows + * downwards. + */ +- return PAGE_ALIGN_DOWN(mmap_upper_limit(rlim_stack) - rnd); ++ #ifdef CONFIG_IGLOO ++ if(igloo_task_size) { ++ return PAGE_ALIGN_DOWN(igloo_task_size - rnd); ++ } else { ++ return PAGE_ALIGN_DOWN(mmap_upper_limit(rlim_stack) - rnd); ++ } ++ #endif + #else + unsigned long gap = rlim_stack->rlim_cur; + unsigned long pad = stack_guard_gap; diff --git a/patches/6.13/0019-add-syscalls.h.patch b/patches/6.13/0019-add-syscalls.h.patch new file mode 100644 index 0000000..2f13508 --- /dev/null +++ b/patches/6.13/0019-add-syscalls.h.patch @@ -0,0 +1,163 @@ +From cda5584e72c3325d41ba492b58672968e896c7ad Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:22:08 -0400 +Subject: [PATCH 19/37] add syscalls.h + +--- + include/linux/syscalls.h | 119 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 109 insertions(+), 10 deletions(-) + +diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h +index c6333204d..09d13ebf2 100644 +--- a/include/linux/syscalls.h ++++ b/include/linux/syscalls.h +@@ -213,14 +213,68 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event) + } + #endif + ++#ifdef CONFIG_IGLOO ++#include ++/* === Igloo Interception Hooks and Helpers === */ ++ ++/* Pointers to the actual hook functions */ ++extern igloo_syscall_enter_t igloo_syscall_enter_hook; ++extern igloo_syscall_return_t igloo_syscall_return_hook; ++#else /* CONFIG_IGLOO not defined */ ++ ++/* Define stubs or original macros if Igloo is disabled */ ++#define igloo_syscall_enter_hook NULL ++#define igloo_syscall_return_hook NULL ++#endif /* CONFIG_IGLOO */ ++ ++ ++/* --- Definition for __PROTECT --- */ ++#ifndef __PROTECT ++#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) ++#endif /* __PROTECT */ + #ifndef SYSCALL_DEFINE0 +-#define SYSCALL_DEFINE0(sname) \ +- SYSCALL_METADATA(_##sname, 0); \ +- asmlinkage long sys_##sname(void); \ +- ALLOW_ERROR_INJECTION(sys_##sname, ERRNO); \ +- asmlinkage long sys_##sname(void) ++#define SYSCALL_DEFINE0(name) \ ++ SYSCALL_METADATA(_##name, 0); \ ++ asmlinkage long sys_##name(void); \ ++ ALLOW_ERROR_INJECTION(sys_##name, ERRNO); \ ++ static inline long __do_sys_##name(void); /* Declare impl */ \ ++ asmlinkage long sys_##name(void) \ ++ { \ ++ const char *syscall_basename = __stringify(name); /* Base name */ \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ /* Array for arguments (empty for 0 args) */ \ ++ unsigned long args_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ \ ++ /* === Igloo Enter Hook === */ \ ++ if (igloo_syscall_enter_hook) { \ ++ /* Pass NULL for setter func for 0-arg syscalls */ \ ++ skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, 0, args_array, NULL); \ ++ } \ ++ \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_sys_##name(); \ ++ } \ ++ \ ++ /* === Igloo Return Hook === */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(syscall_basename, ret, 0, args_array); \ ++ } \ ++ \ ++ /* Argument protection and final return */ \ ++ __PROTECT(0, ret); /* Protect for 0 args */ \ ++ return ret; \ ++ } \ ++ /* User's syscall code defines the __do_sys_##name function */ \ ++ static inline long __do_sys_##name(void) + #endif /* SYSCALL_DEFINE0 */ + ++ ++/* --- Modified SYSCALL_DEFINE1..6 & SYSCALL_DEFINEx --- */ ++/* Pass base name AND internal (_name) to SYSCALL_DEFINEx */ + #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) + #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) + #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) +@@ -229,12 +283,13 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event) + #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) + + #define SYSCALL_DEFINE_MAXARGS 6 ++#define SYSCALL_DEFINEx(x, name, ...) \ ++ SYSCALL_METADATA(name, x, __VA_ARGS__) \ ++ __SYSCALL_DEFINEx(x, name, __VA_ARGS__) + +-#define SYSCALL_DEFINEx(x, sname, ...) \ +- SYSCALL_METADATA(sname, x, __VA_ARGS__) \ +- __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) + +-#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) ++/* --- Modified __SYSCALL_DEFINEx --- */ ++/* Note: Added 'name' (base name) parameter */ + + /* + * The asmlinkage stub is aliased to a function named __se_sys_*() which +@@ -249,12 +304,56 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event) + asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ + __attribute__((alias(__stringify(__se_sys##name)))); \ + ALLOW_ERROR_INJECTION(sys##name, ERRNO); \ ++ /* Implementation function declaration */ \ + static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ ++ /* Setter function definition: Body generated using __SC_GEN_SETTER_BODY_WRAPPER */ \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { \ ++ __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); \ ++ } \ ++ /* Sign-extended wrapper function definition */ \ + asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ +- long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ ++ const char *syscall_basename = __stringify(name); /* Base name */ \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ /* Declare ONE array for argument pointers */ \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ \ ++ /* Populate args_ptr_array with ADDRESSES for the enter hook */ \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ \ ++ /* === Igloo Enter Hook === */ \ ++ if (igloo_syscall_enter_hook) { \ ++ /* Pass setter function pointer unconditionally (except for 0 args) */ \ ++ /* The hook is responsible for handling const args correctly. */ \ ++ skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, x, \ ++ args_ptr_array, __igloo_set_args##name); \ ++ } \ ++ \ ++ if (skip) { \ ++ /* Syscall skipped by hook */ \ ++ ret = skip_ret; \ ++ } else { \ ++ /* Execute actual syscall implementation */ \ ++ /* Arguments used here are potentially modified */ \ ++ /* by the enter hook via the setter function */ \ ++ ret = __do_sys##name(__MAP(x, __SC_CAST, __VA_ARGS__));\ ++ } \ ++ \ ++ /* Original type tests */ \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ ++ \ ++ /* === Igloo Return Hook === */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(syscall_basename, ret, x, \ ++ args_ptr_array); \ ++ } \ ++ \ ++ /* Argument protection and final return */ \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ + } \ diff --git a/patches/6.13/0020-syscall_wrapper-add-x86-support.patch b/patches/6.13/0020-syscall_wrapper-add-x86-support.patch new file mode 100644 index 0000000..0ff322f --- /dev/null +++ b/patches/6.13/0020-syscall_wrapper-add-x86-support.patch @@ -0,0 +1,152 @@ +From 7320d35c161e6f562f0d3bb7a924e23010d1d6c1 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:23:18 -0400 +Subject: [PATCH 20/37] syscall_wrapper: add x86 support + +--- + arch/x86/include/asm/syscall_wrapper.h | 101 +++++++++++++++++++++++-- + 1 file changed, 94 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/include/asm/syscall_wrapper.h b/arch/x86/include/asm/syscall_wrapper.h +index 7e88705e90..341f635fa6 100644 +--- a/arch/x86/include/asm/syscall_wrapper.h ++++ b/arch/x86/include/asm/syscall_wrapper.h +@@ -192,21 +192,64 @@ extern long __ia32_sys_ni_syscall(const struct pt_regs *regs); + * of them. + */ + #define COMPAT_SYSCALL_DEFINE0(name) \ +- static long \ +- __do_compat_sys_##name(const struct pt_regs *__unused); \ ++ static inline long ___do_compat_sys_##name(const struct pt_regs *__unused); \ ++ static long __do_compat_sys_##name(const struct pt_regs *__unused);\ + __IA32_COMPAT_SYS_STUB0(name) \ + __X32_COMPAT_SYS_STUB0(name) \ +- static long \ +- __do_compat_sys_##name(const struct pt_regs *__unused) ++ static long __do_compat_sys_##name(const struct pt_regs *__unused) \ ++ { \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(name), &skip_ret, 0, \ ++ args_ptr_array, NULL); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = ___do_compat_sys_##name(__unused); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(name), ret, 0, args_ptr_array); \ ++ } \ ++ return ret; \ ++ } \ ++ static inline long ___do_compat_sys_##name(const struct pt_regs *__unused) + + #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ + static long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ ++ void __igloo_set_args_compat##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args_compat##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); } \ + __IA32_COMPAT_SYS_STUBx(x, name, __VA_ARGS__) \ + __X32_COMPAT_SYS_STUBx(x, name, __VA_ARGS__) \ + static long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ +- return __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__));\ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(name), &skip_ret, x, \ ++ args_ptr_array, __igloo_set_args_compat##name); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(name), ret, x, args_ptr_array); \ ++ } \ ++ return ret; \ + } \ + static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +@@ -223,11 +266,32 @@ extern long __ia32_sys_ni_syscall(const struct pt_regs *regs); + #define __SYSCALL_DEFINEx(x, name, ...) \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); } \ + __X64_SYS_STUBx(x, name, __VA_ARGS__) \ + __IA32_SYS_STUBx(x, name, __VA_ARGS__) \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ +- long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(name), &skip_ret, x, \ ++ args_ptr_array, __igloo_set_args##name); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(name), ret, x, args_ptr_array); \ ++ } \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ +@@ -243,10 +307,33 @@ extern long __ia32_sys_ni_syscall(const struct pt_regs *regs); + */ + #define SYSCALL_DEFINE0(sname) \ + SYSCALL_METADATA(_##sname, 0); \ ++ static inline long ___do_sys_##sname(const struct pt_regs *__unused); \ + static long __do_sys_##sname(const struct pt_regs *__unused); \ + __X64_SYS_STUB0(sname) \ + __IA32_SYS_STUB0(sname) \ +- static long __do_sys_##sname(const struct pt_regs *__unused) ++ static long __do_sys_##sname(const struct pt_regs *__unused) \ ++ { \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(sname), &skip_ret, 0, \ ++ args_ptr_array, NULL); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = ___do_sys_##sname(__unused); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(sname), ret, 0, args_ptr_array); \ ++ } \ ++ return ret; \ ++ } \ ++ static inline long ___do_sys_##sname(const struct pt_regs *__unused) + + #define COND_SYSCALL(name) \ + __X64_COND_SYSCALL(name) \ diff --git a/patches/6.13/0021-syscall_wrapper.h-add-arm64-support.patch b/patches/6.13/0021-syscall_wrapper.h-add-arm64-support.patch new file mode 100644 index 0000000..a20e425 --- /dev/null +++ b/patches/6.13/0021-syscall_wrapper.h-add-arm64-support.patch @@ -0,0 +1,169 @@ +From 249761eb3875931ac67b6f9bd847692ba3c5fd63 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:24:00 -0400 +Subject: [PATCH 21/37] syscall_wrapper.h: add arm64 support + +--- + arch/arm64/include/asm/syscall_wrapper.h | 108 +++++++++++++++++++++-- + 1 file changed, 102 insertions(+), 6 deletions(-) + +diff --git a/arch/arm64/include/asm/syscall_wrapper.h b/arch/arm64/include/asm/syscall_wrapper.h +index abb57bc543..943a10213e 100644 +--- a/arch/arm64/include/asm/syscall_wrapper.h ++++ b/arch/arm64/include/asm/syscall_wrapper.h +@@ -9,6 +9,13 @@ + #define __ASM_SYSCALL_WRAPPER_H + + #include ++// === Igloo Interception Support === ++#include ++ ++#ifdef CONFIG_IGLOO ++extern igloo_syscall_enter_t igloo_syscall_enter_hook; ++extern igloo_syscall_return_t igloo_syscall_return_hook; ++#endif + + #define SC_ARM64_REGS_TO_ARGS(x, ...) \ + __MAP(x,__SC_ARGS \ +@@ -20,6 +27,9 @@ + #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long __arm64_compat_sys##name(const struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__arm64_compat_sys##name, ERRNO); \ ++ void __igloo_set_args_compat##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args_compat##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); } \ + static long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ + asmlinkage long __arm64_compat_sys##name(const struct pt_regs *regs) \ +@@ -28,14 +38,56 @@ + } \ + static long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ +- return __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(name), &skip_ret, x, \ ++ args_ptr_array, __igloo_set_args_compat##name); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(name), ret, x, args_ptr_array); \ ++ } \ ++ return ret; \ + } \ + static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + + #define COMPAT_SYSCALL_DEFINE0(sname) \ +- asmlinkage long __arm64_compat_sys_##sname(const struct pt_regs *__unused); \ ++ asmlinkage inline long ___arm64_compat_sys_##sname(const struct pt_regs *__unused); \ + ALLOW_ERROR_INJECTION(__arm64_compat_sys_##sname, ERRNO); \ +- asmlinkage long __arm64_compat_sys_##sname(const struct pt_regs *__unused) ++ asmlinkage long __arm64_compat_sys_##sname(const struct pt_regs *__unused);\ ++ asmlinkage long __arm64_compat_sys_##sname(const struct pt_regs *__unused) \ ++ { \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(sname), &skip_ret, 0, \ ++ args_ptr_array, NULL); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = ___arm64_compat_sys_##sname(__unused); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(sname), ret, 0, args_ptr_array); \ ++ } \ ++ return ret; \ ++ } \ ++ asmlinkage inline long ___arm64_compat_sys_##sname(const struct pt_regs *__unused) + + #define COND_SYSCALL_COMPAT(name) \ + asmlinkage long __arm64_compat_sys_##name(const struct pt_regs *regs); \ +@@ -49,6 +101,9 @@ + #define __SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long __arm64_sys##name(const struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__arm64_sys##name, ERRNO); \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); } \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ + asmlinkage long __arm64_sys##name(const struct pt_regs *regs) \ +@@ -57,7 +112,25 @@ + } \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ +- long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(name), &skip_ret, x, \ ++ args_ptr_array, __igloo_set_args##name); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(name), ret, x, args_ptr_array); \ ++ } \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ +@@ -66,9 +139,32 @@ + + #define SYSCALL_DEFINE0(sname) \ + SYSCALL_METADATA(_##sname, 0); \ +- asmlinkage long __arm64_sys_##sname(const struct pt_regs *__unused); \ ++ asmlinkage inline long ___arm64_sys_##sname(const struct pt_regs *__unused); \ + ALLOW_ERROR_INJECTION(__arm64_sys_##sname, ERRNO); \ +- asmlinkage long __arm64_sys_##sname(const struct pt_regs *__unused) ++ asmlinkage long __arm64_sys_##sname(const struct pt_regs *__unused);\ ++ asmlinkage long __arm64_sys_##sname(const struct pt_regs *__unused) \ ++ { \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(sname), &skip_ret, 0, \ ++ args_ptr_array, NULL); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = ___arm64_sys_##sname(__unused); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(sname), ret, 0, args_ptr_array); \ ++ } \ ++ return ret; \ ++ } \ ++ asmlinkage inline long ___arm64_sys_##sname(const struct pt_regs *__unused) + + #define COND_SYSCALL(name) \ + asmlinkage long __arm64_sys_##name(const struct pt_regs *regs); \ diff --git a/patches/6.13/0022-syscall_wrapper.h-add-riscv-support.patch b/patches/6.13/0022-syscall_wrapper.h-add-riscv-support.patch new file mode 100644 index 0000000..b3783a1 --- /dev/null +++ b/patches/6.13/0022-syscall_wrapper.h-add-riscv-support.patch @@ -0,0 +1,166 @@ +From 23f42b21b0638b85f0dd27a369cb23ae55084e73 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:24:36 -0400 +Subject: [PATCH 22/37] syscall_wrapper.h: add riscv support + +--- + arch/riscv/include/asm/syscall_wrapper.h | 109 +++++++++++++++++++++-- + 1 file changed, 103 insertions(+), 6 deletions(-) + +diff --git a/arch/riscv/include/asm/syscall_wrapper.h b/arch/riscv/include/asm/syscall_wrapper.h +index ac80216549..01393a6574 100644 +--- a/arch/riscv/include/asm/syscall_wrapper.h ++++ b/arch/riscv/include/asm/syscall_wrapper.h +@@ -10,6 +10,14 @@ + + #include + ++// === Igloo Interception Support === ++#include ++ ++#ifdef CONFIG_IGLOO ++extern igloo_syscall_enter_t igloo_syscall_enter_hook; ++extern igloo_syscall_return_t igloo_syscall_return_hook; ++#endif ++ + asmlinkage long __riscv_sys_ni_syscall(const struct pt_regs *); + + #ifdef CONFIG_64BIT +@@ -50,10 +58,32 @@ asmlinkage long __riscv_sys_ni_syscall(const struct pt_regs *); + #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long __riscv_compat_sys##name(const struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__riscv_compat_sys##name, ERRNO); \ ++ void __igloo_set_args_compat##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args_compat##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); } \ + static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ + __SYSCALL_SE_DEFINEx(x, compat_sys, name, __VA_ARGS__) \ + { \ +- return __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(name), &skip_ret, x, \ ++ args_ptr_array, __igloo_set_args_compat##name); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(name), ret, x, args_ptr_array); \ ++ } \ ++ return ret; \ + } \ + asmlinkage long __riscv_compat_sys##name(const struct pt_regs *regs) \ + { \ +@@ -62,9 +92,32 @@ asmlinkage long __riscv_sys_ni_syscall(const struct pt_regs *); + static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + + #define COMPAT_SYSCALL_DEFINE0(sname) \ +- asmlinkage long __riscv_compat_sys_##sname(const struct pt_regs *__unused); \ ++ asmlinkage inline long ___riscv_compat_sys_##sname(const struct pt_regs *__unused); \ + ALLOW_ERROR_INJECTION(__riscv_compat_sys_##sname, ERRNO); \ +- asmlinkage long __riscv_compat_sys_##sname(const struct pt_regs *__unused) ++ asmlinkage long __riscv_compat_sys_##sname(const struct pt_regs *__unused); \ ++ asmlinkage long __riscv_compat_sys_##sname(const struct pt_regs *__unused) \ ++ { \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(sname), &skip_ret, 0, \ ++ args_ptr_array, NULL); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = ___riscv_compat_sys_##sname(__unused); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(sname), ret, 0, args_ptr_array); \ ++ } \ ++ return ret; \ ++ } \ ++ asmlinkage inline long ___riscv_compat_sys_##sname(const struct pt_regs *__unused) + + #define COND_SYSCALL_COMPAT(name) \ + asmlinkage long __weak __riscv_compat_sys_##name(const struct pt_regs *regs); \ +@@ -78,11 +131,32 @@ asmlinkage long __riscv_sys_ni_syscall(const struct pt_regs *); + #define __SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long __riscv_sys##name(const struct pt_regs *regs); \ + ALLOW_ERROR_INJECTION(__riscv_sys##name, ERRNO); \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); } \ + static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ + __SYSCALL_SE_DEFINEx(x, sys, name, __VA_ARGS__) \ + { \ +- long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(name), &skip_ret, x, \ ++ args_ptr_array, __igloo_set_args##name); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ ++ } \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(name), ret, x, args_ptr_array); \ ++ } \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ + } \ +@@ -94,9 +168,32 @@ asmlinkage long __riscv_sys_ni_syscall(const struct pt_regs *); + + #define SYSCALL_DEFINE0(sname) \ + SYSCALL_METADATA(_##sname, 0); \ +- asmlinkage long __riscv_sys_##sname(const struct pt_regs *__unused); \ ++ asmlinkage inline long ___riscv_sys_##sname(const struct pt_regs *__unused); \ + ALLOW_ERROR_INJECTION(__riscv_sys_##sname, ERRNO); \ +- asmlinkage long __riscv_sys_##sname(const struct pt_regs *__unused) ++ asmlinkage long __riscv_sys_##sname(const struct pt_regs *__unused);\ ++ asmlinkage long __riscv_sys_##sname(const struct pt_regs *__unused) \ ++ { \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook(__stringify(sname), &skip_ret, 0, \ ++ args_ptr_array, NULL); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = ___riscv_sys_##sname(__unused); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(__stringify(sname), ret, 0, args_ptr_array); \ ++ } \ ++ return ret; \ ++ } \ ++ asmlinkage inline long ___riscv_sys_##sname(const struct pt_regs *__unused) + + #define COND_SYSCALL(name) \ + asmlinkage long __weak __riscv_sys_##name(const struct pt_regs *regs); \ diff --git a/patches/6.13/0023-binfmt_elf.c-add-igloo_task_size-support.patch b/patches/6.13/0023-binfmt_elf.c-add-igloo_task_size-support.patch new file mode 100644 index 0000000..3efb6c7 --- /dev/null +++ b/patches/6.13/0023-binfmt_elf.c-add-igloo_task_size-support.patch @@ -0,0 +1,42 @@ +From 1589ba3d2809d326412cfe5fe963f9c05529a535 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:29:33 -0400 +Subject: [PATCH 23/37] binfmt_elf.c: add igloo_task_size support + +--- + fs/binfmt_elf.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c +index 106f0e8af1..b3d0003ccb 100644 +--- a/fs/binfmt_elf.c ++++ b/fs/binfmt_elf.c +@@ -49,6 +49,7 @@ + #include + #include + #include ++#include + + #ifndef ELF_COMPAT + #define ELF_COMPAT 0 +@@ -1017,8 +1018,19 @@ static int load_elf_binary(struct linux_binprm *bprm) + + /* Do this so that we can load the interpreter, if need be. We will + change some of these later */ ++ #ifdef CONFIG_IGLOO ++ //Begin for igloo: if we moved the stack, we have to move mmap ++ if(igloo_task_size) { ++ retval = setup_arg_pages(bprm, randomize_stack_top(igloo_task_size), ++ executable_stack); ++ } else { ++ retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), ++ executable_stack); ++ } ++ #else + retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), +- executable_stack); ++ executable_stack); ++ #endif + if (retval < 0) + goto out_free_dentry; + diff --git a/patches/6.13/0024-compat.h-syscalls-support.patch b/patches/6.13/0024-compat.h-syscalls-support.patch new file mode 100644 index 0000000..75b85b7 --- /dev/null +++ b/patches/6.13/0024-compat.h-syscalls-support.patch @@ -0,0 +1,93 @@ +From 0e1b11774302462f841142fb9dc261dcbd12e180 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:31:18 -0400 +Subject: [PATCH 24/37] compat.h: syscalls support + +--- + include/linux/compat.h | 67 ++++++++++++++++++++++++++++++++---------- + 1 file changed, 52 insertions(+), 15 deletions(-) + +diff --git a/include/linux/compat.h b/include/linux/compat.h +index 56cebaff0c..5121820555 100644 +--- a/include/linux/compat.h ++++ b/include/linux/compat.h +@@ -62,27 +62,64 @@ + #define COMPAT_SYSCALL_DEFINE6(name, ...) \ + COMPAT_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) + ++ ++// === Igloo Interception Support === ++#include ++ ++#ifdef CONFIG_IGLOO ++extern igloo_syscall_enter_t igloo_syscall_enter_hook; ++extern igloo_syscall_return_t igloo_syscall_return_hook; ++#endif ++ + /* + * The asmlinkage stub is aliased to a function named __se_compat_sys_*() which + * sign-extends 32-bit ints to longs whenever needed. The actual work is + * done within __do_compat_sys_*(). + */ + #ifndef COMPAT_SYSCALL_DEFINEx +-#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ +- __diag_push(); \ +- __diag_ignore(GCC, 8, "-Wattribute-alias", \ +- "Type aliasing is used to sanitize syscall arguments");\ +- asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ +- __attribute__((alias(__stringify(__se_compat_sys##name)))); \ +- ALLOW_ERROR_INJECTION(compat_sys##name, ERRNO); \ +- static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ +- asmlinkage long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ +- asmlinkage long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ +- { \ +- long ret = __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__));\ +- __MAP(x,__SC_TEST,__VA_ARGS__); \ +- return ret; \ +- } \ ++#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ ++ __diag_push(); \ ++ __diag_ignore(GCC, 8, "-Wattribute-alias", \ ++ "Type aliasing is used to sanitize syscall arguments"); \ ++ asmlinkage long compat_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__)) \ ++ __attribute__((alias(__stringify(__se_compat_sys##name)))); \ ++ ALLOW_ERROR_INJECTION(compat_sys##name, ERRNO); \ ++ void __igloo_set_args_compat##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]); \ ++ void __igloo_set_args_compat##name(const unsigned long args_ptr_array[], const __le64 new_args_le64[]) \ ++ { __SC_GEN_SETTER_BODY_WRAPPER(x, __VA_ARGS__); } \ ++ static inline long __do_compat_sys##name( \ ++ __MAP(x, __SC_DECL, __VA_ARGS__)); \ ++ asmlinkage long __se_compat_sys##name( \ ++ __MAP(x, __SC_LONG, __VA_ARGS__)); \ ++ asmlinkage long __se_compat_sys##name( \ ++ __MAP(x, __SC_LONG, __VA_ARGS__)) \ ++ { \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ unsigned long args_ptr_array[IGLOO_SYSCALL_MAXARGS] = { 0 }; \ ++ __SC_ASSIGN_ADDR_WRAPPER(x, args_ptr_array, __VA_ARGS__); \ ++ /* Igloo enter hook */ \ ++ if (igloo_syscall_enter_hook) { \ ++ skip = igloo_syscall_enter_hook( \ ++ __stringify(name), &skip_ret, x, \ ++ args_ptr_array, \ ++ __igloo_set_args_compat##name); \ ++ } \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_compat_sys##name( \ ++ __MAP(x, __SC_DELOUSE, __VA_ARGS__)); \ ++ } \ ++ /* Igloo return hook */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook( \ ++ __stringify(name), ret, x, args_ptr_array); \ ++ } \ ++ __MAP(x,__SC_TEST, __VA_ARGS__); \ ++ return ret; \ ++} \ + __diag_pop(); \ + static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + #endif /* COMPAT_SYSCALL_DEFINEx */ diff --git a/patches/6.13/0025-compat.h-add-define0.patch b/patches/6.13/0025-compat.h-add-define0.patch new file mode 100644 index 0000000..02e23a0 --- /dev/null +++ b/patches/6.13/0025-compat.h-add-define0.patch @@ -0,0 +1,79 @@ +From eb4971ad09ede9ff4f669fa6a692e58e9c9a05b2 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 19 Sep 2025 16:57:21 -0400 +Subject: [PATCH 25/37] compat.h: add define0 + +--- + include/linux/compat.h | 48 +++++++++++++++++++++++++++++++++--------- + 1 file changed, 38 insertions(+), 10 deletions(-) + +diff --git a/include/linux/compat.h b/include/linux/compat.h +index 5121820555..c9df1bacd8 100644 +--- a/include/linux/compat.h ++++ b/include/linux/compat.h +@@ -42,11 +42,48 @@ + #define __SC_DELOUSE(t,v) ((__force t)(unsigned long)(v)) + #endif + ++ ++// === Igloo Interception Support === ++#include ++ ++#ifdef CONFIG_IGLOO ++extern igloo_syscall_enter_t igloo_syscall_enter_hook; ++extern igloo_syscall_return_t igloo_syscall_return_hook; ++#endif ++ + #ifndef COMPAT_SYSCALL_DEFINE0 + #define COMPAT_SYSCALL_DEFINE0(name) \ + asmlinkage long compat_sys_##name(void); \ + ALLOW_ERROR_INJECTION(compat_sys_##name, ERRNO); \ +- asmlinkage long compat_sys_##name(void) ++ asmlinkage long compat_sys_##name(void) \ ++ { \ ++ const char *syscall_basename = __stringify(name); /* Base name */ \ ++ long ret; \ ++ bool skip = false; \ ++ long skip_ret = 0; \ ++ /* Array for arguments (empty for 0 args) */ \ ++ unsigned long args_array[IGLOO_SYSCALL_MAXARGS] = {0}; \ ++ /* === Igloo Enter Hook === */ \ ++ if (igloo_syscall_enter_hook) { \ ++ /* Pass NULL for setter func for 0-arg syscalls */ \ ++ skip = igloo_syscall_enter_hook(syscall_basename, &skip_ret, 0, args_array, NULL); \ ++ } \ ++ \ ++ if (skip) { \ ++ ret = skip_ret; \ ++ } else { \ ++ ret = __do_compat_sys_##name(); \ ++ } \ ++ \ ++ /* === Igloo Return Hook === */ \ ++ if (igloo_syscall_return_hook) { \ ++ ret = igloo_syscall_return_hook(syscall_basename, ret, 0, args_array); \ ++ } \ ++ \ ++ return ret ; \ ++ } \ ++ static inline long __do_compat_sys_##name(void) ++ + #endif /* COMPAT_SYSCALL_DEFINE0 */ + + #define COMPAT_SYSCALL_DEFINE1(name, ...) \ +@@ -62,15 +99,6 @@ + #define COMPAT_SYSCALL_DEFINE6(name, ...) \ + COMPAT_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) + +- +-// === Igloo Interception Support === +-#include +- +-#ifdef CONFIG_IGLOO +-extern igloo_syscall_enter_t igloo_syscall_enter_hook; +-extern igloo_syscall_return_t igloo_syscall_return_hook; +-#endif +- + /* + * The asmlinkage stub is aliased to a function named __se_compat_sys_*() which + * sign-extends 32-bit ints to longs whenever needed. The actual work is diff --git a/patches/6.13/0026-igloo_weaksyms-fix-up-igloo_ioctl-type.patch b/patches/6.13/0026-igloo_weaksyms-fix-up-igloo_ioctl-type.patch new file mode 100644 index 0000000..0ccab2a --- /dev/null +++ b/patches/6.13/0026-igloo_weaksyms-fix-up-igloo_ioctl-type.patch @@ -0,0 +1,28 @@ +From 4799b41002283a8608f919dd775d2945839ff168 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 22 Sep 2025 10:58:40 -0400 +Subject: [PATCH 26/37] igloo_weaksyms: fix up igloo_ioctl type + +--- + drivers/igloobase/igloo_weaksyms.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/igloobase/igloo_weaksyms.c b/drivers/igloobase/igloo_weaksyms.c +index 8498d15500..33e83342d2 100644 +--- a/drivers/igloobase/igloo_weaksyms.c ++++ b/drivers/igloobase/igloo_weaksyms.c +@@ -75,12 +75,12 @@ void igloo_hc_open(int dfd, const char __user *filename, struct open_how *how) + } + EXPORT_SYMBOL(igloo_hc_open); + +-void (*igloo_ioctl_module)(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp); ++void (*igloo_ioctl_module)(int error, struct file *filp, unsigned int cmd); + void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp); + void igloo_ioctl(int error, struct inode *inode, struct file *filp, unsigned int cmd, void __user *argp) + { + if (igloo_ioctl_module) { +- igloo_ioctl_module(error, inode, filp, cmd, argp); ++ igloo_ioctl_module(error, filp, cmd); + } else { + // printk(KERN_EMERG "igloo_ioctl: unimplemented\n"); + } diff --git a/patches/6.13/0028-socket.c-add-CONFIG_IGLOO.patch b/patches/6.13/0028-socket.c-add-CONFIG_IGLOO.patch new file mode 100644 index 0000000..beff637 --- /dev/null +++ b/patches/6.13/0028-socket.c-add-CONFIG_IGLOO.patch @@ -0,0 +1,52 @@ +From 0c2a6e83a8a90559251c0f559d0400eaa3eb2ada Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Mon, 22 Sep 2025 11:01:31 -0400 +Subject: [PATCH 28/37] socket.c: add CONFIG_IGLOO + +--- + net/socket.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/net/socket.c b/net/socket.c +index b69372eef4..a3c64a8773 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -628,13 +628,17 @@ struct socket *sock_alloc(void) + } + EXPORT_SYMBOL(sock_alloc); + ++#ifdef CONFIG_IGLOO + // forward declare igloo_sock_release + void igloo_sock_release(struct socket *sock); ++#endif + + static void __sock_release(struct socket *sock, struct inode *inode) + { + const struct proto_ops *ops = READ_ONCE(sock->ops); ++ #ifdef CONFIG_IGLOO + igloo_sock_release(sock); ++ #endif + if (ops) { + struct module *owner = ops->owner; + +@@ -1819,8 +1823,10 @@ SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol, + return __sys_socketpair(family, type, protocol, usockvec); + } + ++#ifdef CONFIG_IGLOO + // forward declare igloo_sock_bind + void igloo_sock_bind(struct socket *sock, struct sockaddr_storage *address); ++#endif + + int __sys_bind_socket(struct socket *sock, struct sockaddr_storage *address, + int addrlen) +@@ -1833,7 +1839,9 @@ int __sys_bind_socket(struct socket *sock, struct sockaddr_storage *address, + err = READ_ONCE(sock->ops)->bind(sock, + (struct sockaddr *)address, + addrlen); ++#ifdef CONFIG_IGLOO + igloo_sock_bind(sock, address); ++#endif + } + return err; + } diff --git a/patches/6.13/0029-compat.h-add-forward-declaration.patch b/patches/6.13/0029-compat.h-add-forward-declaration.patch new file mode 100644 index 0000000..eaf786c --- /dev/null +++ b/patches/6.13/0029-compat.h-add-forward-declaration.patch @@ -0,0 +1,21 @@ +From 774d5b6e347ab2d4e251f1bc80f967244343e1e9 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Tue, 23 Sep 2025 11:28:33 -0400 +Subject: [PATCH 29/37] compat.h: add forward declaration + +--- + include/linux/compat.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/linux/compat.h b/include/linux/compat.h +index c9df1bacd8..89a9dd5563 100644 +--- a/include/linux/compat.h ++++ b/include/linux/compat.h +@@ -53,6 +53,7 @@ extern igloo_syscall_return_t igloo_syscall_return_hook; + + #ifndef COMPAT_SYSCALL_DEFINE0 + #define COMPAT_SYSCALL_DEFINE0(name) \ ++ static inline long __do_compat_sys_##name(void); \ + asmlinkage long compat_sys_##name(void); \ + ALLOW_ERROR_INJECTION(compat_sys_##name, ERRNO); \ + asmlinkage long compat_sys_##name(void) \ diff --git a/patches/6.13/0030-igloo-debug-calls-now-pr_emerg-when-enabled.patch b/patches/6.13/0030-igloo-debug-calls-now-pr_emerg-when-enabled.patch new file mode 100644 index 0000000..c0080ff --- /dev/null +++ b/patches/6.13/0030-igloo-debug-calls-now-pr_emerg-when-enabled.patch @@ -0,0 +1,55 @@ +From 996b6807416e345bd242774ef7808c63e274b524 Mon Sep 17 00:00:00 2001 +From: Zak Estrada +Date: Fri, 24 Oct 2025 16:21:37 -0400 +Subject: [PATCH 30/37] igloo debug calls now pr_emerg when enabled + +--- + drivers/igloobase/igloo_args.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/igloobase/igloo_args.c b/drivers/igloobase/igloo_args.c +index bdb65d1fbc..2f274130b2 100644 +--- a/drivers/igloobase/igloo_args.c ++++ b/drivers/igloobase/igloo_args.c +@@ -75,14 +75,14 @@ static int __init early_igloo_debug_modules(char *p) + // Special case: "all" enables all modules + if (!strcmp(p, "all")) { + memset(&igloo_debug, 1, sizeof(igloo_debug)); +- pr_warn_once("IGLOO: Debug enabled for all modules\n"); ++ pr_emerg_once("IGLOO: Debug enabled for all modules\n"); + return 0; + } + + // Special case: "none" disables all modules (default) + if (!strcmp(p, "none")) { + memset(&igloo_debug, 0, sizeof(igloo_debug)); +- pr_warn_once("IGLOO: Debug disabled for all modules\n"); ++ pr_emerg_once("IGLOO: Debug disabled for all modules\n"); + return 0; + } + +@@ -100,14 +100,14 @@ static int __init early_igloo_debug_modules(char *p) + igloo_debug.osi = true; + else if (!strcmp(token, "all")){ + memset(&igloo_debug, 1, sizeof(igloo_debug)); +- pr_warn_once("IGLOO: Debug enabled for all modules\n"); ++ pr_emerg_once("IGLOO: Debug enabled for all modules\n"); + return 0; + } + else +- pr_warn("IGLOO: Unknown debug module: %s\n", token); ++ pr_emerg("IGLOO: Unknown debug module: %s\n", token); + } + +- pr_warn_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d\n", ++ pr_emerg_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d\n", + igloo_debug.portal, igloo_debug.uprobe, igloo_debug.vma, + igloo_debug.syscall, igloo_debug.osi); + +@@ -115,4 +115,4 @@ static int __init early_igloo_debug_modules(char *p) + } + + early_param("igloo_debug", early_igloo_debug_modules); +-EXPORT_SYMBOL(igloo_debug); +\ No newline at end of file ++EXPORT_SYMBOL(igloo_debug); diff --git a/patches/6.13/0031-open.c-fix-fd-in-igloo_open_hc-30.patch b/patches/6.13/0031-open.c-fix-fd-in-igloo_open_hc-30.patch new file mode 100644 index 0000000..471032e --- /dev/null +++ b/patches/6.13/0031-open.c-fix-fd-in-igloo_open_hc-30.patch @@ -0,0 +1,35 @@ +From add8c7c3c723a8d301c9860154150259f2d0f5ec Mon Sep 17 00:00:00 2001 +From: Benjamin Levy <153011444+be32826@users.noreply.github.com> +Date: Fri, 7 Nov 2025 13:52:24 -0500 +Subject: [PATCH 31/37] open.c: fix fd in igloo_open_hc() (#30) + +--- + fs/open.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/fs/open.c b/fs/open.c +index ed0828cb91..621a52d310 100644 +--- a/fs/open.c ++++ b/fs/open.c +@@ -1401,9 +1401,6 @@ static long do_sys_openat2(int dfd, const char __user *filename, + tmp = getname(filename); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); +- #ifdef CONFIG_IGLOO +- igloo_hc_open(dfd, tmp, fd); +- #endif + fd = get_unused_fd_flags(how->flags); + if (fd >= 0) { + struct file *f = do_filp_open(dfd, tmp, &op); +@@ -1414,6 +1411,11 @@ static long do_sys_openat2(int dfd, const char __user *filename, + fd_install(fd, f); + } + } ++ ++ #ifdef CONFIG_IGLOO ++ igloo_hc_open(dfd, tmp, fd); ++ #endif ++ + putname(tmp); + return fd; + } diff --git a/patches/6.13/0032-kprobe-options.patch b/patches/6.13/0032-kprobe-options.patch new file mode 100644 index 0000000..82260af --- /dev/null +++ b/patches/6.13/0032-kprobe-options.patch @@ -0,0 +1,78 @@ +From e50e45a458ab04a5ba6fbab41ecce787785b21f5 Mon Sep 17 00:00:00 2001 +From: Zak Estrada +Date: Tue, 6 Jan 2026 12:24:32 -0500 +Subject: [PATCH 32/37] kprobe options + +--- + drivers/igloobase/igloo_args.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/drivers/igloobase/igloo_args.c b/drivers/igloobase/igloo_args.c +index 2f274130b2..3674da3f42 100644 +--- a/drivers/igloobase/igloo_args.c ++++ b/drivers/igloobase/igloo_args.c +@@ -19,11 +19,11 @@ static int __init early_igloo_task_size(char *p) + { + unsigned long task_size; + if (kstrtoul(p, 0, &task_size) < 0 ) { +- pr_warn("Could not parse igloo_task_size parameter %s\n", p); ++ pr_emerg("Could not parse igloo_task_size parameter %s\n", p); + return -1; + } + igloo_task_size = task_size; +- pr_warn_once("Using igloo_task_size: 0x%lx\n", igloo_task_size); ++ pr_emerg_once("Using igloo_task_size: 0x%lx\n", igloo_task_size); + return 0; + } + early_param("igloo_task_size", early_igloo_task_size); +@@ -35,11 +35,11 @@ static int __init early_igloo_block_halt(char *p) + { + unsigned long block_halt; + if (kstrtoul(p, 0, &block_halt) < 0 ) { +- pr_warn("Could not parse igloo_block_halt parameter %s. Set to 0 (default) or 1\n", p); ++ pr_emerg("Could not parse igloo_block_halt parameter %s. Set to 0 (default) or 1\n", p); + return -1; + } + igloo_block_halt = (block_halt > 0); +- pr_warn_once("Using igloo_block_halt: %d\n", igloo_block_halt); ++ pr_emerg_once("Using igloo_block_halt: %d\n", igloo_block_halt); + return 0; + } + +@@ -53,6 +53,7 @@ struct igloo_debug_config { + bool vma; // Enable debug for VMA tracking + bool syscall; // Enable debug for syscall tracking + bool osi; // Enable debug for OSI features ++ bool kprobe; // Enable debug for kprobe module + }; + + // Global debug configuration +@@ -62,6 +63,7 @@ struct igloo_debug_config igloo_debug = { + .vma = false, + .syscall = false, + .osi = false, ++ .kprobe = false, + }; + + // Parse comma-separated list of modules to enable debug logging for +@@ -98,6 +100,8 @@ static int __init early_igloo_debug_modules(char *p) + igloo_debug.syscall = true; + else if (!strcmp(token, "osi")) + igloo_debug.osi = true; ++ else if (!strcmp(token, "kprobe")) ++ igloo_debug.kprobe = true; + else if (!strcmp(token, "all")){ + memset(&igloo_debug, 1, sizeof(igloo_debug)); + pr_emerg_once("IGLOO: Debug enabled for all modules\n"); +@@ -107,9 +111,9 @@ static int __init early_igloo_debug_modules(char *p) + pr_emerg("IGLOO: Unknown debug module: %s\n", token); + } + +- pr_emerg_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d\n", ++ pr_emerg_once("IGLOO: Debug modules - portal:%d uprobe:%d vma:%d syscall:%d osi:%d kprobe: %d\n", + igloo_debug.portal, igloo_debug.uprobe, igloo_debug.vma, +- igloo_debug.syscall, igloo_debug.osi); ++ igloo_debug.syscall, igloo_debug.osi, igloo_debug.kprobe); + + return 0; + } diff --git a/patches/6.13/0033-kprobes-fix-single-step-bug-on-mips.patch b/patches/6.13/0033-kprobes-fix-single-step-bug-on-mips.patch new file mode 100644 index 0000000..a4065a1 --- /dev/null +++ b/patches/6.13/0033-kprobes-fix-single-step-bug-on-mips.patch @@ -0,0 +1,126 @@ +From e4e7363c0635ef64e11d16168b8e228e7b977481 Mon Sep 17 00:00:00 2001 +From: Zak Estrada +Date: Sat, 10 Jan 2026 13:28:57 -0500 +Subject: [PATCH 33/37] kprobes: fix single step bug on mips + +--- + arch/mips/kernel/kprobes.c | 85 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 83 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/kernel/kprobes.c b/arch/mips/kernel/kprobes.c +index dc39f5b3fb..35db842b47 100644 +--- a/arch/mips/kernel/kprobes.c ++++ b/arch/mips/kernel/kprobes.c +@@ -25,6 +25,63 @@ + + #include "probes-common.h" + ++/* BEGIN IGLOO PATCH */ ++#include ++#include ++ ++struct kprobe_slot_map { ++ struct hlist_node hnode; ++ unsigned long slot1; /* &p->ainsn.insn[1] */ ++ struct kprobe *kp; ++}; ++ ++static DEFINE_HASHTABLE(kprobe_slot_ht, 10); /* 1024 buckets */ ++static DEFINE_SPINLOCK(kprobe_slot_lock); ++ ++static void kprobe_slot_add(struct kprobe *p) ++{ ++ struct kprobe_slot_map *e; ++ ++ e = kmalloc(sizeof(*e), GFP_KERNEL); /* registration context, safe */ ++ if (!e) ++ return; ++ ++ e->slot1 = (unsigned long)&p->ainsn.insn[1]; ++ e->kp = p; ++ ++ spin_lock(&kprobe_slot_lock); ++ hash_add(kprobe_slot_ht, &e->hnode, e->slot1); ++ spin_unlock(&kprobe_slot_lock); ++} ++ ++static void kprobe_slot_del(struct kprobe *p) ++{ ++ unsigned long key = (unsigned long)&p->ainsn.insn[1]; ++ struct kprobe_slot_map *e; ++ ++ spin_lock(&kprobe_slot_lock); ++ hash_for_each_possible(kprobe_slot_ht, e, hnode, key) { ++ if (e->slot1 == key && e->kp == p) { ++ hash_del(&e->hnode); ++ kfree(e); ++ break; ++ } ++ } ++ spin_unlock(&kprobe_slot_lock); ++} ++ ++static struct kprobe *kprobe_slot_lookup(unsigned long slot1) ++{ ++ struct kprobe_slot_map *e; ++ ++ hash_for_each_possible(kprobe_slot_ht, e, hnode, slot1) { ++ if (e->slot1 == slot1) ++ return e->kp; ++ } ++ return NULL; ++} ++/* END IGLOO PATCH */ ++ + static const union mips_instruction breakpoint_insn = { + .b_format = { + .opcode = spec_op, +@@ -109,6 +166,7 @@ int arch_prepare_kprobe(struct kprobe *p) + ret = -ENOMEM; + goto out; + } ++ kprobe_slot_add(p); /* IGLOO PATCH */ + + /* + * In the kprobe->ainsn.insn[] array we store the original +@@ -153,6 +211,7 @@ NOKPROBE_SYMBOL(arch_disarm_kprobe); + void arch_remove_kprobe(struct kprobe *p) + { + if (p->ainsn.insn) { ++ kprobe_slot_del(p); /* IGLOO PATCH */ + free_insn_slot(p->ainsn.insn, 0); + p->ainsn.insn = NULL; + } +@@ -381,8 +440,30 @@ static inline int post_kprobe_handler(struct pt_regs *regs) + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + +- if (!cur) +- return 0; ++ /* BEGIN IGLOO PATCH ++ * // Replaces this: ++ * if (!cur) ++ * return 0; ++ * ++ */ ++ if (!cur) { ++ /* Recover probe from slot1 address (SSTEPBP happens at &ainsn[1]) */ ++ struct kprobe *p = kprobe_slot_lookup(regs->cp0_epc); ++ if (p) { ++ __this_cpu_write(current_kprobe, p); ++ cur = p; ++ ++ /* ++ * Make sure status is consistent for resume_execution(). ++ * We were in SS mode anyway per your logs. ++ */ ++ if (!(kcb->kprobe_status & KPROBE_HIT_SS)) ++ kcb->kprobe_status = KPROBE_HIT_SS; ++ } else { ++ return 0; /* truly not ours */ ++ } ++ } ++ /* END IGLOO PATCH */ + + if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; diff --git a/patches/6.13/0034-igloo-fix-igloo_task_size.patch b/patches/6.13/0034-igloo-fix-igloo_task_size.patch new file mode 100644 index 0000000..d572128 --- /dev/null +++ b/patches/6.13/0034-igloo-fix-igloo_task_size.patch @@ -0,0 +1,280 @@ +From f5bc757c51854c2b571bc44b5441a21afb05ee15 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 9 May 2026 16:53:34 -0400 +Subject: [PATCH 34/37] igloo: fix igloo_task_size + +--- + arch/arm/mm/mmap.c | 27 +++++++++++++++++++++------ + arch/loongarch/mm/mmap.c | 17 +++++++++++++---- + arch/mips/mm/mmap.c | 17 +++++++++++++---- + fs/binfmt_elf.c | 17 +++++++++++------ + mm/util.c | 5 +++++ + 5 files changed, 63 insertions(+), 20 deletions(-) + +diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c +index 3dbb383c26..d34983be00 100644 +--- a/arch/arm/mm/mmap.c ++++ b/arch/arm/mm/mmap.c +@@ -12,6 +12,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + + #define COLOUR_ALIGN(addr,pgoff) \ + ((((addr)+SHMLBA-1)&~(SHMLBA-1)) + \ +@@ -36,6 +39,12 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, + int do_align = 0; + int aliasing = cache_is_vipt_aliasing(); + struct vm_unmapped_area_info info = {}; ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + + /* + * We only need to do colour alignment if either the I or D +@@ -54,7 +63,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, + return addr; + } + +- if (len > TASK_SIZE) ++ if (len > task_size) + return -ENOMEM; + + if (addr) { +@@ -64,14 +73,14 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, + addr = PAGE_ALIGN(addr); + + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && ++ if (task_size - len >= addr && + (!vma || addr + len <= vm_start_gap(vma))) + return addr; + } + + info.length = len; + info.low_limit = mm->mmap_base; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + info.align_mask = do_align ? (PAGE_MASK & (SHMLBA - 1)) : 0; + info.align_offset = pgoff << PAGE_SHIFT; + return vm_unmapped_area(&info); +@@ -88,6 +97,12 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + int do_align = 0; + int aliasing = cache_is_vipt_aliasing(); + struct vm_unmapped_area_info info = {}; ++ unsigned long task_size = TASK_SIZE; ++ ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif + + /* + * We only need to do colour alignment if either the I or D +@@ -97,7 +112,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + do_align = filp || (flags & MAP_SHARED); + + /* requested length too big for entire address space */ +- if (len > TASK_SIZE) ++ if (len > task_size) + return -ENOMEM; + + if (flags & MAP_FIXED) { +@@ -114,7 +129,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + else + addr = PAGE_ALIGN(addr); + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && ++ if (task_size - len >= addr && + (!vma || addr + len <= vm_start_gap(vma))) + return addr; + } +@@ -137,7 +152,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + VM_BUG_ON(addr != -ENOMEM); + info.flags = 0; + info.low_limit = mm->mmap_base; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + addr = vm_unmapped_area(&info); + } + +diff --git a/arch/loongarch/mm/mmap.c b/arch/loongarch/mm/mmap.c +index 914e82ff3f..b1e9d97685 100644 +--- a/arch/loongarch/mm/mmap.c ++++ b/arch/loongarch/mm/mmap.c +@@ -8,6 +8,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + + #define SHM_ALIGN_MASK (SHMLBA - 1) + +@@ -26,13 +29,19 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + unsigned long addr = addr0; + int do_color_align; + struct vm_unmapped_area_info info = {}; ++ unsigned long task_size = TASK_SIZE; + +- if (unlikely(len > TASK_SIZE)) ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif ++ ++ if (unlikely(len > task_size)) + return -ENOMEM; + + if (flags & MAP_FIXED) { + /* Even MAP_FIXED mappings must reside within TASK_SIZE */ +- if (TASK_SIZE - len < addr) ++ if (task_size - len < addr) + return -EINVAL; + + /* +@@ -57,7 +66,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + addr = PAGE_ALIGN(addr); + + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && ++ if (task_size - len >= addr && + (!vma || addr + len <= vm_start_gap(vma))) + return addr; + } +@@ -84,7 +93,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + } + + info.low_limit = mm->mmap_base; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + return vm_unmapped_area(&info); + } + +diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c +index 5d2a122578..af7b3d6b2b 100644 +--- a/arch/mips/mm/mmap.c ++++ b/arch/mips/mm/mmap.c +@@ -16,6 +16,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + + unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */ + EXPORT_SYMBOL(shm_align_mask); +@@ -35,13 +38,19 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + unsigned long addr = addr0; + int do_color_align; + struct vm_unmapped_area_info info = {}; ++ unsigned long task_size = TASK_SIZE; + +- if (unlikely(len > TASK_SIZE)) ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ task_size = igloo_task_size; ++#endif ++ ++ if (unlikely(len > task_size)) + return -ENOMEM; + + if (flags & MAP_FIXED) { + /* Even MAP_FIXED mappings must reside within TASK_SIZE */ +- if (TASK_SIZE - len < addr) ++ if (task_size - len < addr) + return -EINVAL; + + /* +@@ -66,7 +75,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + addr = PAGE_ALIGN(addr); + + vma = find_vma(mm, addr); +- if (TASK_SIZE - len >= addr && ++ if (task_size - len >= addr && + (!vma || addr + len <= vm_start_gap(vma))) + return addr; + } +@@ -93,7 +102,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, + } + + info.low_limit = mm->mmap_base; +- info.high_limit = TASK_SIZE; ++ info.high_limit = task_size; + return vm_unmapped_area(&info); + } + +diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c +index b3d0003ccb..cafdebcad6 100644 +--- a/fs/binfmt_elf.c ++++ b/fs/binfmt_elf.c +@@ -49,7 +49,14 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO + #include ++#define BAD_ADDR(x) (unlikely((unsigned long)(x) >= (igloo_task_size ? igloo_task_size : TASK_SIZE))) ++#define IGLOO_TASK_SIZE (igloo_task_size ? igloo_task_size : TASK_SIZE) ++#else ++#define BAD_ADDR(x) (unlikely((unsigned long)(x) >= TASK_SIZE)) ++#define IGLOO_TASK_SIZE TASK_SIZE ++#endif + + #ifndef ELF_COMPAT + #define ELF_COMPAT 0 +@@ -109,8 +116,6 @@ static struct linux_binfmt elf_format = { + #endif + }; + +-#define BAD_ADDR(x) (unlikely((unsigned long)(x) >= TASK_SIZE)) +- + /* + * We need to explicitly zero any trailing portion of the page that follows + * p_filesz when it ends before the page ends (e.g. bss), otherwise this +@@ -700,8 +705,8 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, + k = load_addr + eppnt->p_vaddr; + if (BAD_ADDR(k) || + eppnt->p_filesz > eppnt->p_memsz || +- eppnt->p_memsz > TASK_SIZE || +- TASK_SIZE - eppnt->p_memsz < k) { ++ eppnt->p_memsz > IGLOO_TASK_SIZE || ++ IGLOO_TASK_SIZE - eppnt->p_memsz < k) { + error = -ENOMEM; + goto out; + } +@@ -1221,8 +1226,8 @@ static int load_elf_binary(struct linux_binprm *bprm) + * <= p_memsz so it is only necessary to check p_memsz. + */ + if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz || +- elf_ppnt->p_memsz > TASK_SIZE || +- TASK_SIZE - elf_ppnt->p_memsz < k) { ++ elf_ppnt->p_memsz > IGLOO_TASK_SIZE || ++ IGLOO_TASK_SIZE - elf_ppnt->p_memsz < k) { + /* set_brk can never work. Avoid overflows. */ + retval = -EINVAL; + goto out_free_dentry; +diff --git a/mm/util.c b/mm/util.c +index f5d5f5dac3..72386af4b7 100644 +--- a/mm/util.c ++++ b/mm/util.c +@@ -462,6 +462,11 @@ static unsigned long mmap_base(unsigned long rnd, struct rlimit *rlim_stack) + else if (gap > MAX_GAP) + gap = MAX_GAP; + ++#ifdef CONFIG_IGLOO ++ if (igloo_task_size) ++ return PAGE_ALIGN(igloo_task_size - gap - rnd); ++#endif ++ + return PAGE_ALIGN(STACK_TOP - gap - rnd); + #endif + } diff --git a/patches/6.13/0036-igloo-move-into-procfs.patch b/patches/6.13/0036-igloo-move-into-procfs.patch new file mode 100644 index 0000000..5b1c7b5 --- /dev/null +++ b/patches/6.13/0036-igloo-move-into-procfs.patch @@ -0,0 +1,180 @@ +From f21401b2552d26ff3c30ff83cf50dfc7ca3d70ff Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sun, 17 May 2026 13:21:30 -0400 +Subject: [PATCH 36/37] igloo: move into procfs + +--- + fs/proc/base.c | 129 +++++++++++++++++++++++++++++++++++++++- + include/linux/proc_fs.h | 5 ++ + 2 files changed, 131 insertions(+), 3 deletions(-) + +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 0edf14a984..e4f691768e 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -71,6 +71,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -160,6 +161,112 @@ struct pid_entry { + union proc_op op; + }; + ++#ifdef CONFIG_IGLOO ++struct igloo_proc_pid_entry { ++ struct list_head list; ++ struct proc_dir_entry *pde; ++ const struct file_operations *fop; ++}; ++ ++static LIST_HEAD(igloo_proc_pid_entries); ++static DEFINE_MUTEX(igloo_proc_pid_entries_lock); ++ ++struct proc_dir_entry *igloo_proc_create_pid_data(const char *name, umode_t mode, ++ const struct file_operations *fop, ++ void *data) ++{ ++ struct proc_dir_entry *parent = NULL; ++ struct igloo_proc_pid_entry *entry; ++ struct proc_dir_entry *pde; ++ size_t len; ++ ++ if (!name || !name[0] || strchr(name, '/')) ++ return NULL; ++ if ((mode & S_IFMT) == 0) ++ mode |= S_IFREG; ++ if ((mode & S_IALLUGO) == 0) ++ mode |= S_IRUGO; ++ if (!S_ISREG(mode) || !fop) ++ return NULL; ++ ++ len = strlen(name); ++ ++ mutex_lock(&igloo_proc_pid_entries_lock); ++ list_for_each_entry(entry, &igloo_proc_pid_entries, list) { ++ pde = entry->pde; ++ if (pde->namelen == len && !memcmp(pde->name, name, len)) { ++ pde->mode = mode; ++ pde->data = data; ++ entry->fop = fop; ++ mutex_unlock(&igloo_proc_pid_entries_lock); ++ return pde; ++ } ++ } ++ mutex_unlock(&igloo_proc_pid_entries_lock); ++ ++ entry = kzalloc(sizeof(*entry), GFP_KERNEL); ++ if (!entry) ++ return NULL; ++ pde = proc_create_reg(name, mode, &parent, data); ++ if (!pde) { ++ kfree(entry); ++ return NULL; ++ } ++ ++ entry->pde = pde; ++ entry->fop = fop; ++ ++ mutex_lock(&igloo_proc_pid_entries_lock); ++ list_add(&entry->list, &igloo_proc_pid_entries); ++ mutex_unlock(&igloo_proc_pid_entries_lock); ++ ++ return pde; ++} ++EXPORT_SYMBOL_GPL(igloo_proc_create_pid_data); ++ ++static struct dentry *igloo_proc_pid_instantiate(struct dentry *dentry, ++ struct task_struct *task, ++ struct igloo_proc_pid_entry *entry) ++{ ++ struct proc_dir_entry *pde = entry->pde; ++ struct inode *inode; ++ ++ inode = proc_pid_make_inode(dentry->d_sb, task, pde->mode); ++ if (!inode) ++ return ERR_PTR(-ENOENT); ++ ++ pde_get(pde); ++ PROC_I(inode)->pde = pde; ++ inode->i_private = pde->data; ++ inode->i_fop = entry->fop; ++ if (pde->size) ++ inode->i_size = pde->size; ++ pid_update_inode(task, inode); ++ d_set_d_op(dentry, &pid_dentry_operations); ++ return d_splice_alias(inode, dentry); ++} ++ ++static struct dentry *igloo_proc_pid_lookup(struct dentry *dentry, ++ struct task_struct *task) ++{ ++ struct igloo_proc_pid_entry *entry; ++ struct dentry *res = ERR_PTR(-ENOENT); ++ ++ mutex_lock(&igloo_proc_pid_entries_lock); ++ list_for_each_entry(entry, &igloo_proc_pid_entries, list) { ++ if (entry->pde->namelen != dentry->d_name.len) ++ continue; ++ if (!memcmp(entry->pde->name, dentry->d_name.name, ++ dentry->d_name.len)) { ++ res = igloo_proc_pid_instantiate(dentry, task, entry); ++ break; ++ } ++ } ++ mutex_unlock(&igloo_proc_pid_entries_lock); ++ return res; ++} ++#endif ++ + #define NOD(NAME, MODE, IOP, FOP, OP) { \ + .name = (NAME), \ + .len = sizeof(NAME) - 1, \ +@@ -3442,9 +3549,25 @@ struct pid *tgid_pidfd_to_pid(const struct file *file) + + static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) + { +- return proc_pident_lookup(dir, dentry, +- tgid_base_stuff, +- tgid_base_stuff + ARRAY_SIZE(tgid_base_stuff)); ++ struct dentry *res; ++#ifdef CONFIG_IGLOO ++ struct task_struct *task; ++#endif ++ ++ res = proc_pident_lookup(dir, dentry, ++ tgid_base_stuff, ++ tgid_base_stuff + ARRAY_SIZE(tgid_base_stuff)); ++#ifdef CONFIG_IGLOO ++ if (!IS_ERR(res) || PTR_ERR(res) != -ENOENT) ++ return res; ++ ++ task = get_proc_task(dir); ++ if (!task) ++ return res; ++ res = igloo_proc_pid_lookup(dentry, task); ++ put_task_struct(task); ++#endif ++ return res; + } + + static const struct inode_operations proc_tgid_base_inode_operations = { +diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h +index 0b2a898544..43c1f9cf33 100644 +--- a/include/linux/proc_fs.h ++++ b/include/linux/proc_fs.h +@@ -107,6 +107,11 @@ extern struct proc_dir_entry *proc_create_data(const char *, umode_t, + struct proc_dir_entry *, + const struct proc_ops *, + void *); ++#ifdef CONFIG_IGLOO ++struct proc_dir_entry *igloo_proc_create_pid_data(const char *, umode_t, ++ const struct file_operations *, ++ void *); ++#endif + + struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct proc_ops *proc_ops); + extern void proc_set_size(struct proc_dir_entry *, loff_t); diff --git a/patches/6.13/0037-igloo-move-into-signal-handler.patch b/patches/6.13/0037-igloo-move-into-signal-handler.patch new file mode 100644 index 0000000..012f954 --- /dev/null +++ b/patches/6.13/0037-igloo-move-into-signal-handler.patch @@ -0,0 +1,69 @@ +From 8f25e989e12e2fca57aec18413ad157efb452ee4 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sun, 17 May 2026 13:21:37 -0400 +Subject: [PATCH 37/37] igloo: move into signal handler + +--- + include/linux/igloo_signal.h | 12 ++++++++++++ + kernel/signal.c | 15 +++++++++++++++ + 2 files changed, 27 insertions(+) + create mode 100644 include/linux/igloo_signal.h + +diff --git a/include/linux/igloo_signal.h b/include/linux/igloo_signal.h +new file mode 100644 +index 0000000000..375b5ee68b +--- /dev/null ++++ b/include/linux/igloo_signal.h +@@ -0,0 +1,12 @@ ++#ifndef _LINUX_IGLOO_SIGNAL_H ++#define _LINUX_IGLOO_SIGNAL_H ++ ++#include ++ ++struct task_struct; ++ ++typedef bool (*igloo_signal_deliver_hook_t)(int sig, struct task_struct *task); ++ ++extern igloo_signal_deliver_hook_t igloo_signal_deliver_hook; ++ ++#endif /* _LINUX_IGLOO_SIGNAL_H */ +diff --git a/kernel/signal.c b/kernel/signal.c +index a2afd54303..50a618bd5d 100644 +--- a/kernel/signal.c ++++ b/kernel/signal.c +@@ -43,6 +43,9 @@ + #include + #include + #include ++#ifdef CONFIG_IGLOO ++#include ++#endif + #include + #include + #include +@@ -67,6 +70,11 @@ + + static struct kmem_cache *sigqueue_cachep; + ++#ifdef CONFIG_IGLOO ++igloo_signal_deliver_hook_t igloo_signal_deliver_hook; ++EXPORT_SYMBOL_GPL(igloo_signal_deliver_hook); ++#endif ++ + int print_fatal_signals __read_mostly; + + static void __user *sig_handler(struct task_struct *t, int sig) +@@ -1049,6 +1057,13 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info, + lockdep_assert_held(&t->sighand->siglock); + + result = TRACE_SIGNAL_IGNORED; ++#ifdef CONFIG_IGLOO ++ if (sig && igloo_signal_deliver_hook && ++ igloo_signal_deliver_hook(sig, t)) { ++ ret = 0; ++ goto ret; ++ } ++#endif + if (!prepare_signal(sig, t, force)) + goto ret; + diff --git a/patches/6.13/series b/patches/6.13/series new file mode 100644 index 0000000..19fae24 --- /dev/null +++ b/patches/6.13/series @@ -0,0 +1,33 @@ +6.13/0005-Net-disallow-changing-bridge-mac-addrs-from-firmadyn.patch +core/add-hypercall.h.patch +6.13/0007-reboot.c-add-igloo_block_halt.patch +6.13/0008-socket.c-add-igloo_socket_hc.patch +core/add-igloo.h.patch +6.13/0010-open.c-add-igloo_open_hc.patch +6.13/0011-sys.c-add-igloo_hc_newuname.patch +6.13/0012-namespace.c-add-igloo_should_block_mount.patch +6.13/0013-ioctl.c-add-igloo_ioctl_hc.patch +6.13/0014-igloobase-add-driver.patch +6.13/0015-exec.c-add-igloo_task_size.patch +6.13/0016-mmap.c-add-igloo_task_size.patch +6.13/0017-util.c-add-igloo_task_size.patch +core/add-igloo_syscall_macros.h.patch +6.13/0019-add-syscalls.h.patch +6.13/0020-syscall_wrapper-add-x86-support.patch +6.13/0021-syscall_wrapper.h-add-arm64-support.patch +6.13/0022-syscall_wrapper.h-add-riscv-support.patch +6.13/0023-binfmt_elf.c-add-igloo_task_size-support.patch +6.13/0024-compat.h-syscalls-support.patch +6.13/0025-compat.h-add-define0.patch +6.13/0026-igloo_weaksyms-fix-up-igloo_ioctl-type.patch +core/igloo.h-drop-declarations-for-forward-declarations.patch +6.13/0028-socket.c-add-CONFIG_IGLOO.patch +6.13/0029-compat.h-add-forward-declaration.patch +6.13/0030-igloo-debug-calls-now-pr_emerg-when-enabled.patch +6.13/0031-open.c-fix-fd-in-igloo_open_hc-30.patch +6.13/0032-kprobe-options.patch +6.13/0033-kprobes-fix-single-step-bug-on-mips.patch +6.13/0034-igloo-fix-igloo_task_size.patch +core/update-hypercall.h.patch +6.13/0036-igloo-move-into-procfs.patch +6.13/0037-igloo-move-into-signal-handler.patch diff --git a/patches/README.md b/patches/README.md new file mode 100644 index 0000000..f76f3d8 --- /dev/null +++ b/patches/README.md @@ -0,0 +1,70 @@ +# The IGLOO kernel patch series + +The IGLOO kernel delta, carried as an explicit patch series applied to pristine +upstream release tarballs — replacing the two long-lived fork branches of +`rehosting/linux` that used to arrive as git submodules. + +## Layout + +``` +base.json upstream base tag + tarball hash per version +core/ patches byte-identical across every version (5) +4.10/ 6.13/ per-version patches + the `series` file +``` + +Each `series` file lists patch paths relative to `patches/`, in apply order, so +core patches and version-specific ones interleave where the ordering requires. +Blank lines and `#` comments are ignored (quilt convention). + +| version | base | patches | of which shared | +|---|---|---|---| +| 4.10 | `v4.10` | 38 | 5 | +| 6.13 | `v6.13` | 33 | 5 | + +## How much is really shared — read this before assuming + +Of the 23 patches that carry the **same subject** in both versions, only **5 are +byte-identical**. Those 5 are precisely the ones that add *new files* +(`include/hypercall.h`, `include/igloo.h`, `include/igloo_syscall_macros.h`). +Every patch that modifies existing kernel code differs between 4.10 and 6.13, +because the surrounding code differs. + +So the series does **not** collapse the two versions into one maintained copy. +What it does deliver: + +- **Additive work is written once.** New files under `drivers/igloobase/` and + `include/` go in `core/` and apply to every version — this is the class that + igloo_driver #918 falls into. +- **Hook-site work stays per-version**, as it must; it is now visibly per-version + instead of silently duplicated across two branches. +- The old failure mode — fixing a bug on one branch and forgetting the other — + becomes a visible diff between two series rather than an invisible omission. + (It happened: the same fix shipped as PR #29 on 4.10 and #30 on 6.13.) + +## Two things the migration uncovered + +**The 6.13 fork was not based on a release.** `main_6.13` branched five commits +before `v6.13` and then cherry-picked four of them back — `x86: Disable +EXECMEM_ROX support`, `x86/fred: …`, `x86/asm: Make serialize() always_inline`, +and the `Linux 6.13` version commit. All four are upstream, so basing on the +`v6.13` tarball reproduces the same tree with none of them carried locally. + +**`.gitmodules` named the wrong branches.** It declared `branch = main_6.7` for +`linux/6.13` while the pin was actually the head of `main_6.13` — trees 89,775 +commits apart. `git submodule update --remote` would have silently regressed the +kernel. Basing on a tag makes that class of drift unrepresentable. + +## Working on the kernel + +```bash +./scripts/import-series.sh 6.13 /tmp/k613 # tarball + series -> a git tree +cd /tmp/k613 && git commit ... # develop normally +./scripts/export-series.sh 6.13 /tmp/k613 # write the series back +./scripts/verify-series.sh 6.13 refs/remotes/origin/main_6.13 +``` + +`verify-series.sh` is the gate: it applies the series to the pristine tarball and +asserts the result matches the fork branch. Note release tarballs are `git +archive` output and honour `export-ignore`, so they legitimately lack +`.gitattributes`, `.get_maintainer.ignore`, and two `arch/sh` linker scripts — +the check allows exactly those paths and fails on anything else. diff --git a/patches/base.json b/patches/base.json new file mode 100644 index 0000000..5ab6367 --- /dev/null +++ b/patches/base.json @@ -0,0 +1,10 @@ +{ + "4.10": { + "tag": "4.10", + "hash": "sha256-PJXZ8Em9CF5cNG0sd/BjuEJfGRRg/NOun+fpTgR33Es=" + }, + "6.13": { + "tag": "6.13", + "hash": "sha256-553Mbrhmlca6v7B8KGGRK2NdUHXGzRzQVn0eoVX4DW4=" + } +} diff --git a/patches/core/add-hypercall.h.patch b/patches/core/add-hypercall.h.patch new file mode 100644 index 0000000..40ed30a --- /dev/null +++ b/patches/core/add-hypercall.h.patch @@ -0,0 +1,358 @@ +From babe02e6a05eac84f10a6791eca430363cac6220 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 10:59:23 -0400 +Subject: [PATCH 02/38] add hypercall.h + +--- + include/hypercall.h | 341 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 341 insertions(+) + create mode 100644 include/hypercall.h + +diff --git a/include/hypercall.h b/include/hypercall.h +new file mode 100644 +index 0000000000..b3c308a596 +--- /dev/null ++++ b/include/hypercall.h +@@ -0,0 +1,341 @@ ++#ifndef HYPERCALL_H ++#define HYPERCALL_H ++#include // Use standard include path ++ ++static inline unsigned long igloo_hypercall(unsigned long num, unsigned long arg1) { ++#if defined(CONFIG_MIPS) ++ register unsigned long reg0 asm("v0") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ ++ asm volatile( ++ "movz $0, $0, $0" ++ : "+r"(reg0) ++ : "r"(reg1) // num in register v0 ++ : "memory" ++ ); ++ ++ return reg0; ++ ++#elif defined(CONFIG_ARM64) ++ register unsigned long reg0 asm("x8") = num; ++ register unsigned long reg1 asm("x0") = arg1; ++ asm volatile( ++ "msr S0_0_c5_c0_0, xzr \n" ++ : "+r"(reg1) ++ : "r"(reg0) ++ : "memory" ++ ); ++ ++ return reg1; ++ ++#elif defined(CONFIG_ARM) ++ register unsigned long reg0 asm("r7") = num; ++ register unsigned long reg1 asm("r0") = arg1; ++ ++ asm volatile( ++ "mcr p7, 0, r0, c0, c0, 0" ++ : "+r"(reg1) ++ : "r"(reg0) ++ : "memory" ++ ); ++ ++ return reg1; ++ ++#elif defined(CONFIG_X86_64) ++ register unsigned long reg0 asm("rax") = num; ++ register unsigned long reg1 asm("rdi") = arg1; ++ ++ asm volatile( ++ "cpuid" ++ : "+r"(reg0) // hypercall num + return value in rax ++ : "r"(reg1) // arguments ++ : "memory", "rbx", "rcx", "rdx" // No clobber ++ ); ++ ++ return reg0; ++ ++#elif defined(CONFIG_I386) ++ register unsigned long reg0 asm("eax") = num; ++ register unsigned long reg1 asm("edi") = arg1; ++ ++ asm volatile( ++ "cpuid" ++ : "+r"(reg0) // hypercall num + return value in rax ++ : "r"(reg1) // arguments ++ : "memory", "ebx", "ecx", "edx" ++ ); ++ ++ return reg0; ++ ++#elif defined(CONFIG_LOONGARCH) ++ register unsigned long reg0 asm("a7") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ ++ asm volatile( ++ "cpucfg $r0, $r0" ++ : "+r"(reg0) ++ : "r"(reg1) ++ : "memory" // No clobber ++ ); ++ ++ return reg0; ++ ++#elif defined(CONFIG_PPC) ++ register unsigned long reg0 asm("r0") = num; ++ register unsigned long reg1 asm("r3") = arg1; ++ ++ asm volatile( ++ "xori 10, 10, 0" // User-specified instruction - ASSUMED CORRECT TRIGGER ++ : "+r"(reg0) // Input num in r0, Output retval in r0 ++ : "r"(reg1) // Input arg1 in r3 ++ : "memory", "lr", "ctr", // CRITICAL: clobber link and count registers ++ // Clobber volatile condition register fields: ++ "cr0", "cr1", "cr5", "cr6", "cr7", ++ // Clobber volatile GPRs (r4-r12) - r0,r3 handled by constraints: ++ "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" ++ // Note: r2 (TOC) and r13 (thread ptr) are usually non-volatile but check hypervisor docs ++ ); ++ ++ return reg0; ++ ++#elif defined(CONFIG_RISCV) ++ register unsigned long reg0 asm("a7") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ ++ asm volatile( ++ "xori x0, x0, 0" ++ : "+r"(reg1) /* Modified: a0/reg1 is both input and output */ ++ : "r"(reg0) ++ : "memory" ++ ); ++ ++ return reg1; ++ ++#else ++#error "No igloo_hypercall support for architecture" ++#endif ++} ++ ++static inline unsigned long igloo_hypercall2(unsigned long num, unsigned long arg1, unsigned long arg2) { ++#if defined(CONFIG_ARM64) ++ register unsigned long reg0 asm("x8") = num; ++ register unsigned long reg1 asm("x0") = arg1; ++ register unsigned long reg2 asm("x1") = arg2; ++ asm volatile( ++ "msr S0_0_c5_c0_0, xzr \n" ++ : "+r"(reg1) // Input and output ++ : "r"(reg0), "r"(reg2) ++ : "memory" ++ ); ++ return reg1; ++#elif defined(CONFIG_ARM) ++ register unsigned long reg0 asm("r7") = num; ++ register unsigned long reg1 asm("r0") = arg1; ++ register unsigned long reg2 asm("r1") = arg2; ++ ++ asm volatile( ++ "mcr p7, 0, r0, c0, c0, 0" ++ : "+r"(reg1) // Input and output ++ : "r"(reg0), "r"(reg2) ++ : "memory" ++ ); ++ ++ return reg1; ++ ++#elif defined(CONFIG_MIPS) ++ register unsigned long reg0 asm("v0") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ register unsigned long reg2 asm("a1") = arg2; ++ ++ asm volatile( ++ "movz $0, $0, $0" ++ : "+r"(reg0) // Input and output in v0 ++ : "r"(reg1), "r"(reg2) ++ : "memory" ++ ); ++ return reg0; ++#elif defined(CONFIG_X86_64) ++ register unsigned long reg0 asm("rax") = num; ++ register unsigned long reg1 asm("rdi") = arg1; ++ register unsigned long reg2 asm("rsi") = arg2; ++ ++ asm volatile( ++ "cpuid" ++ : "+r"(reg0) // hypercall num + return value in rax ++ : "r"(reg1), "r"(reg2) // arguments ++ : "memory", "rbx", "rcx", "rdx" ++ ); ++ ++ return reg0; ++#elif defined(CONFIG_I386) ++ register unsigned long reg0 asm("eax") = num; ++ register unsigned long reg1 asm("edi") = arg1; ++ register unsigned long reg2 asm("esi") = arg2; ++ ++ asm volatile( ++ "cpuid" ++ : "+r"(reg0) // hypercall num + return value in rax ++ : "r"(reg1), "r"(reg2) // arguments ++ : "memory", "ebx", "ecx", "edx" ++ ); ++ ++ return reg0; ++#elif defined(CONFIG_LOONGARCH) ++ register unsigned long reg0 asm("a7") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ register unsigned long reg2 asm("a1") = arg2; ++ ++ asm volatile( ++ "cpucfg $r0, $r0" ++ : "+r"(reg1) /* a0/reg1 is both input and output */ ++ : "r"(reg0), "r"(reg2) ++ : "memory" ++ ); ++ return reg1; /* Return reg1 (a0) which contains the return value from the hypervisor */ ++#elif defined(CONFIG_PPC) || defined(CONFIG_PPC64) ++ register unsigned long reg0 asm("r0") = num; ++ register unsigned long reg1 asm("r3") = arg1; ++ register unsigned long reg2 asm("r4") = arg2; // Second arg in r4 ++ ++ asm volatile( ++ "xori 10, 10, 0" // User-specified instruction ++ : "+r"(reg1) // Input and output in r3 ++ : "r"(reg0), "r"(reg2) ++ : "memory", "lr", "ctr", ++ "cr0", "cr1", "cr5", "cr6", "cr7", ++ "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" ++ ); ++ return reg1; /* Return reg1 (r3) which contains the return value from the hypervisor */ ++#elif defined(CONFIG_RISCV) ++ register unsigned long reg0 asm("a7") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ register unsigned long reg2 asm("a1") = arg2; ++ ++ asm volatile( ++ "xori x0, x0, 0" ++ : "+r"(reg1) /* a0/reg1 is both input and output */ ++ : "r"(reg0), "r"(reg2) ++ : "memory" ++ ); ++ return reg1; /* Return reg1 (a0) which contains the return value from the hypervisor */ ++#else ++#error "No igloo_hypercall2 support for architecture" ++#endif ++} ++ ++static inline unsigned long igloo_hypercall3(unsigned long num, unsigned long arg1, unsigned long arg2, unsigned long arg3) { ++#if defined(CONFIG_ARM64) ++ register unsigned long reg0 asm("x8") = num; ++ register unsigned long reg1 asm("x0") = arg1; ++ register unsigned long reg2 asm("x1") = arg2; ++ register unsigned long reg3 asm("x2") = arg3; ++ asm volatile( ++ "msr S0_0_c5_c0_0, xzr \n" ++ : "+r"(reg1) // Input and output ++ : "r"(reg0), "r"(reg2), "r"(reg3) ++ : "memory" ++ ); ++ return reg1; ++#elif defined(CONFIG_ARM) ++ register unsigned long reg0 asm("r7") = num; ++ register unsigned long reg1 asm("r0") = arg1; ++ register unsigned long reg2 asm("r1") = arg2; ++ register unsigned long reg3 asm("r2") = arg3; ++ ++ asm volatile( ++ "mcr p7, 0, r0, c0, c0, 0" ++ : "+r"(reg1) // Input and output ++ : "r"(reg0), "r"(reg2), "r"(reg3) ++ : "memory" ++ ); ++ ++ return reg1; ++ ++#elif defined(CONFIG_MIPS) ++ register unsigned long reg0 asm("v0") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ register unsigned long reg2 asm("a1") = arg2; ++ register unsigned long reg3 asm("a2") = arg3; ++ ++ asm volatile( ++ "movz $0, $0, $0" ++ : "+r"(reg0) // Input and output in v0 ++ : "r"(reg1), "r"(reg2), "r"(reg3) ++ : "memory" ++ ); ++ return reg0; ++#elif defined(CONFIG_X86_64) ++ register unsigned long reg0 asm("rax") = num; ++ register unsigned long reg1 asm("rdi") = arg1; ++ register unsigned long reg2 asm("rsi") = arg2; ++ register unsigned long reg3 asm("rdx") = arg3; ++ ++ asm volatile( ++ "cpuid" ++ : "+r"(reg0) // hypercall num + return value in rax ++ : "r"(reg1), "r"(reg2), "r"(reg3) // arguments ++ : "memory", "rbx", "rcx" ++ ); ++ ++ return reg0; ++#elif defined(CONFIG_I386) ++ register unsigned long reg0 asm("eax") = num; ++ register unsigned long reg1 asm("edi") = arg1; ++ register unsigned long reg2 asm("esi") = arg2; ++ register unsigned long reg3 asm("edx") = arg3; ++ ++ asm volatile( ++ "cpuid" ++ : "+r"(reg0) // hypercall num + return value in eax ++ : "r"(reg1), "r"(reg2), "r"(reg3) // arguments ++ : "memory", "ebx", "ecx" ++ ); ++ ++ return reg0; ++#elif defined(CONFIG_LOONGARCH) ++ register unsigned long reg0 asm("a7") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ register unsigned long reg2 asm("a1") = arg2; ++ register unsigned long reg3 asm("a2") = arg3; ++ ++ asm volatile( ++ "cpucfg $r0, $r0" ++ : "+r"(reg1) /* a0/reg1 is both input and output */ ++ : "r"(reg0), "r"(reg2), "r"(reg3) ++ : "memory" ++ ); ++ return reg1; /* Return reg1 (a0) which contains the return value from the hypervisor */ ++#elif defined(CONFIG_PPC) || defined(CONFIG_PPC64) ++ register unsigned long reg0 asm("r0") = num; ++ register unsigned long reg1 asm("r3") = arg1; ++ register unsigned long reg2 asm("r4") = arg2; ++ register unsigned long reg3 asm("r5") = arg3; ++ ++ asm volatile( ++ "xori 10, 10, 0" // User-specified instruction ++ : "+r"(reg1) // Input and output in r3 ++ : "r"(reg0), "r"(reg2), "r"(reg3) ++ : "memory", "lr", "ctr", ++ "cr0", "cr1", "cr5", "cr6", "cr7", ++ "r6", "r7", "r8", "r9", "r10", "r11", "r12" ++ ); ++ return reg1; /* Return reg1 (r3) which contains the return value from the hypervisor */ ++#elif defined(CONFIG_RISCV) ++ register unsigned long reg0 asm("a7") = num; ++ register unsigned long reg1 asm("a0") = arg1; ++ register unsigned long reg2 asm("a1") = arg2; ++ register unsigned long reg3 asm("a2") = arg3; ++ ++ asm volatile( ++ "xori x0, x0, 0" ++ : "+r"(reg1) /* a0/reg1 is both input and output */ ++ : "r"(reg0), "r"(reg2), "r"(reg3) ++ : "memory" ++ ); ++ return reg1; /* Return reg1 (a0) which contains the return value from the hypervisor */ ++#else ++#error "No igloo_hypercall3 support for architecture" ++#endif ++} ++ ++#endif +\ No newline at end of file diff --git a/patches/core/add-igloo.h.patch b/patches/core/add-igloo.h.patch new file mode 100644 index 0000000..b2f78da --- /dev/null +++ b/patches/core/add-igloo.h.patch @@ -0,0 +1,44 @@ +From cf5d2fddb7ae35b113fd61410c363896be1c011e Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Thu, 31 Jul 2025 23:15:33 -0400 +Subject: [PATCH 05/38] add igloo.h + +--- + include/igloo.h | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + create mode 100644 include/igloo.h + +diff --git a/include/igloo.h b/include/igloo.h +new file mode 100644 +index 0000000000..45e1dcd81c +--- /dev/null ++++ b/include/igloo.h +@@ -0,0 +1,27 @@ ++#ifndef _LINUX_IGLOO_H ++#define _LINUX_IGLOO_H ++#include ++#include ++#include ++ ++extern unsigned long igloo_task_size; // mmap.c ++extern bool igloo_block_halt; // reboot.c ++ ++struct user_arg_ptr; // Forward declaration ++struct syscall_metadata; // Forward declaration for syscall metadata functions ++ ++void igloo_sock_release(struct socket *sock); ++void igloo_sock_bind(struct socket *sock, struct sockaddr_storage *address); ++void igloo_hc_newuname(struct new_utsname *name); ++unsigned long igloo_arch_syscall_addr(int nr); ++ ++/* Syscall metadata access functions */ ++struct syscall_metadata *igloo_get_syscall_metadata(int nr); ++int igloo_get_nr_syscalls(void); ++struct syscall_metadata *igloo_get_syscall_metadata_by_index(int index); ++int igloo_get_syscall_metadata_count(void); ++struct syscall_metadata *igloo_get_syscall_metadata_copy(int nr); ++int igloo_get_syscall_metadata_count_copy(void); ++struct syscall_metadata *igloo_get_syscall_metadata_by_index_copy(int index); ++ ++#endif /* _LINUX_IGLOO_H */ +\ No newline at end of file diff --git a/patches/core/add-igloo_syscall_macros.h.patch b/patches/core/add-igloo_syscall_macros.h.patch new file mode 100644 index 0000000..ad6e2e6 --- /dev/null +++ b/patches/core/add-igloo_syscall_macros.h.patch @@ -0,0 +1,116 @@ +From ca08ed92a85242800be647ed485386d871dc9736 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Fri, 1 Aug 2025 11:18:27 -0400 +Subject: [PATCH 13/38] add igloo_syscall_macros.h + +--- + include/igloo_syscall_macros.h | 99 ++++++++++++++++++++++++++++++++++ + 1 file changed, 99 insertions(+) + create mode 100644 include/igloo_syscall_macros.h + +diff --git a/include/igloo_syscall_macros.h b/include/igloo_syscall_macros.h +new file mode 100644 +index 0000000000..e8f41b20fb +--- /dev/null ++++ b/include/igloo_syscall_macros.h +@@ -0,0 +1,99 @@ ++#ifndef _IGLOO_SYSCALL_MACROS_H ++ ++/** ++ * igloo_syscall_setter_t - Typedef for the syscall argument setter function. ++ * @args_ptr_array: Array containing the ADDRESSES of the original syscall ++ * arguments, each cast to unsigned long. ++ * @new_args_le64: Array containing the new argument values, provided as ++ * little-endian 64-bit values. ++ * ++ * This function, generated per syscall, casts the pointers and new values ++ * to the correct types and updates the original arguments. ++ * NOTE: This function is generated even for syscalls with const args ++ * (using a (void *) cast to bypass compile errors). The hook MUST NOT ++ * call this setter with modified values for const arguments. ++ */ ++typedef void (*igloo_syscall_setter_t)(const unsigned long args_ptr_array[], ++ const __le64 new_args_le64[]); ++ ++ ++/** ++ * igloo_syscall_enter_t - Typedef for the enter hook function. ++ * @syscall_name: The string name ("read", "openat", etc.) of the syscall. ++ * @skip_ret_val: Pointer to store the return value if skipping the syscall. ++ * @argc: Number of arguments passed to the syscall. ++ * @args_ptr_array: Array containing the ADDRESSES of the syscall arguments. ++ * @setter_func: Pointer to the type-safe setter function for this specific ++ * syscall, or NULL if argc is 0. The hook can call this ++ * function if it decides to modify arguments, but MUST respect ++ * the const nature of arguments. ++ * ++ * Returns: true to skip the actual syscall execution, false to proceed. ++ */ ++typedef bool (*igloo_syscall_enter_t)(const char *syscall_name, ++ long *skip_ret_val, ++ int argc, ++ const unsigned long args_ptr_array[], ++ igloo_syscall_setter_t setter_func); ++ ++/** ++ * igloo_syscall_return_t - Typedef for the return hook function. ++ * @syscall_name: The string name ("read", "openat", etc.) of the syscall. ++ * @orig_ret: The original return value from the syscall (or skip value). ++ * @argc: Number of arguments passed to the syscall. ++ * @args_val_array: Array containing the final VALUES of the syscall arguments ++ * (after potential modification by enter hook), each cast ++ * to unsigned long. ++ * ++ * Returns: The potentially modified return value for the syscall. ++ */ ++typedef long (*igloo_syscall_return_t)(const char *syscall_name, ++ long retval, int argc, ++ const unsigned long args[]); ++ ++#ifdef CONFIG_IGLOO ++extern igloo_syscall_enter_t igloo_syscall_enter_hook; ++extern igloo_syscall_return_t igloo_syscall_return_hook; ++#endif ++ ++ ++#define IGLOO_SYSCALL_MAXARGS 6 ++ ++#define __SC_ASSIGN_ADDR_ITER_0(arr, ...) ++#define __SC_ASSIGN_ADDR_ITER_1(arr, t1, a1) arr[0] = (unsigned long)&a1; ++#define __SC_ASSIGN_ADDR_ITER_2(arr, t1, a1, t2, a2) arr[0] = (unsigned long)&a1; arr[1] = (unsigned long)&a2; ++#define __SC_ASSIGN_ADDR_ITER_3(arr, t1, a1, t2, a2, t3, a3) arr[0] = (unsigned long)&a1; arr[1] = (unsigned long)&a2; arr[2] = (unsigned long)&a3; ++#define __SC_ASSIGN_ADDR_ITER_4(arr, t1, a1, t2, a2, t3, a3, t4, a4) arr[0] = (unsigned long)&a1; arr[1] = (unsigned long)&a2; arr[2] = (unsigned long)&a3; arr[3] = (unsigned long)&a4; ++#define __SC_ASSIGN_ADDR_ITER_5(arr, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) arr[0] = (unsigned long)&a1; arr[1] = (unsigned long)&a2; arr[2] = (unsigned long)&a3; arr[3] = (unsigned long)&a4; arr[4] = (unsigned long)&a5; ++#define __SC_ASSIGN_ADDR_ITER_6(arr, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6) arr[0] = (unsigned long)&a1; arr[1] = (unsigned long)&a2; arr[2] = (unsigned long)&a3; arr[3] = (unsigned long)&a4; arr[4] = (unsigned long)&a5; arr[5] = (unsigned long)&a6; ++ ++ ++ ++#define __SC_CONDITIONAL_ASSIGN(idx, type) \ ++ { type __temp_val = (type)(uintptr_t)(new_args_le64[idx]); \ ++ memcpy((void *)(uintptr_t)args_ptr_array[idx], &__temp_val, sizeof(type)); \ ++ (void)0; } ++ ++#define __SC_GEN_SETTER_BODY_ITER_0(...) ++#define __SC_GEN_SETTER_BODY_ITER_1(t1, a1) __SC_CONDITIONAL_ASSIGN(0, t1); ++#define __SC_GEN_SETTER_BODY_ITER_2(t1, a1, t2, a2) __SC_CONDITIONAL_ASSIGN(0, t1); __SC_CONDITIONAL_ASSIGN(1, t2); ++#define __SC_GEN_SETTER_BODY_ITER_3(t1, a1, t2, a2, t3, a3) __SC_CONDITIONAL_ASSIGN(0, t1); __SC_CONDITIONAL_ASSIGN(1, t2); __SC_CONDITIONAL_ASSIGN(2, t3); ++#define __SC_GEN_SETTER_BODY_ITER_4(t1, a1, t2, a2, t3, a3, t4, a4) __SC_CONDITIONAL_ASSIGN(0, t1); __SC_CONDITIONAL_ASSIGN(1, t2); __SC_CONDITIONAL_ASSIGN(2, t3); __SC_CONDITIONAL_ASSIGN(3, t4); ++#define __SC_GEN_SETTER_BODY_ITER_5(t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) __SC_CONDITIONAL_ASSIGN(0, t1); __SC_CONDITIONAL_ASSIGN(1, t2); __SC_CONDITIONAL_ASSIGN(2, t3); __SC_CONDITIONAL_ASSIGN(3, t4); __SC_CONDITIONAL_ASSIGN(4, t5); ++#define __SC_GEN_SETTER_BODY_ITER_6(t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6) __SC_CONDITIONAL_ASSIGN(0, t1); __SC_CONDITIONAL_ASSIGN(1, t2); __SC_CONDITIONAL_ASSIGN(2, t3); __SC_CONDITIONAL_ASSIGN(3, t4); __SC_CONDITIONAL_ASSIGN(4, t5); __SC_CONDITIONAL_ASSIGN(5, t6); ++ ++ ++#ifndef CONFIG_IGLOO ++#define igloo_syscall_enter_hook NULL ++#define igloo_syscall_return_hook NULL ++#define __SC_ASSIGN_ADDR_WRAPPER(nr, arr, ...) do {} while (0) ++#define __SC_GEN_SETTER_BODY_WRAPPER(nr, ...) do {} while (0) ++#else ++#define __SC_ASSIGN_ADDR_WRAPPER(nr, arr, ...) \ ++ __SC_ASSIGN_ADDR_ITER_##nr(arr, __VA_ARGS__) ++#define __SC_GEN_SETTER_BODY_WRAPPER(nr, ...) \ ++ __SC_GEN_SETTER_BODY_ITER_##nr(__VA_ARGS__) ++ ++#endif ++ ++#endif +\ No newline at end of file diff --git a/patches/core/igloo.h-drop-declarations-for-forward-declarations.patch b/patches/core/igloo.h-drop-declarations-for-forward-declarations.patch new file mode 100644 index 0000000..ab12474 --- /dev/null +++ b/patches/core/igloo.h-drop-declarations-for-forward-declarations.patch @@ -0,0 +1,23 @@ +From 3f0f02b4c0872f1d7cf61a6282800e9b98c233b4 Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sat, 20 Sep 2025 18:10:12 -0400 +Subject: [PATCH 28/38] igloo.h: drop declarations for forward declarations + +--- + include/igloo.h | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/include/igloo.h b/include/igloo.h +index 45e1dcd81c..1de6832e81 100644 +--- a/include/igloo.h ++++ b/include/igloo.h +@@ -10,9 +10,6 @@ extern bool igloo_block_halt; // reboot.c + struct user_arg_ptr; // Forward declaration + struct syscall_metadata; // Forward declaration for syscall metadata functions + +-void igloo_sock_release(struct socket *sock); +-void igloo_sock_bind(struct socket *sock, struct sockaddr_storage *address); +-void igloo_hc_newuname(struct new_utsname *name); + unsigned long igloo_arch_syscall_addr(int nr); + + /* Syscall metadata access functions */ diff --git a/patches/core/update-hypercall.h.patch b/patches/core/update-hypercall.h.patch new file mode 100644 index 0000000..5515b86 --- /dev/null +++ b/patches/core/update-hypercall.h.patch @@ -0,0 +1,424 @@ +From 341a2d7402d62274d6ec4c615f6833e14658feba Mon Sep 17 00:00:00 2001 +From: Luke Craig +Date: Sun, 17 May 2026 09:09:26 -0400 +Subject: [PATCH 36/38] update hypercall.h + +--- + include/hypercall.h | 334 +++++++++----------------------------------- + 1 file changed, 69 insertions(+), 265 deletions(-) + +diff --git a/include/hypercall.h b/include/hypercall.h +index b3c308a596..0986c4da38 100644 +--- a/include/hypercall.h ++++ b/include/hypercall.h +@@ -1,238 +1,25 @@ + #ifndef HYPERCALL_H + #define HYPERCALL_H +-#include // Use standard include path + +-static inline unsigned long igloo_hypercall(unsigned long num, unsigned long arg1) { +-#if defined(CONFIG_MIPS) +- register unsigned long reg0 asm("v0") = num; +- register unsigned long reg1 asm("a0") = arg1; +- +- asm volatile( +- "movz $0, $0, $0" +- : "+r"(reg0) +- : "r"(reg1) // num in register v0 +- : "memory" +- ); +- +- return reg0; +- +-#elif defined(CONFIG_ARM64) +- register unsigned long reg0 asm("x8") = num; +- register unsigned long reg1 asm("x0") = arg1; +- asm volatile( +- "msr S0_0_c5_c0_0, xzr \n" +- : "+r"(reg1) +- : "r"(reg0) +- : "memory" +- ); +- +- return reg1; +- +-#elif defined(CONFIG_ARM) +- register unsigned long reg0 asm("r7") = num; +- register unsigned long reg1 asm("r0") = arg1; +- +- asm volatile( +- "mcr p7, 0, r0, c0, c0, 0" +- : "+r"(reg1) +- : "r"(reg0) +- : "memory" +- ); +- +- return reg1; +- +-#elif defined(CONFIG_X86_64) +- register unsigned long reg0 asm("rax") = num; +- register unsigned long reg1 asm("rdi") = arg1; +- +- asm volatile( +- "cpuid" +- : "+r"(reg0) // hypercall num + return value in rax +- : "r"(reg1) // arguments +- : "memory", "rbx", "rcx", "rdx" // No clobber +- ); +- +- return reg0; +- +-#elif defined(CONFIG_I386) +- register unsigned long reg0 asm("eax") = num; +- register unsigned long reg1 asm("edi") = arg1; +- +- asm volatile( +- "cpuid" +- : "+r"(reg0) // hypercall num + return value in rax +- : "r"(reg1) // arguments +- : "memory", "ebx", "ecx", "edx" +- ); +- +- return reg0; +- +-#elif defined(CONFIG_LOONGARCH) +- register unsigned long reg0 asm("a7") = num; +- register unsigned long reg1 asm("a0") = arg1; +- +- asm volatile( +- "cpucfg $r0, $r0" +- : "+r"(reg0) +- : "r"(reg1) +- : "memory" // No clobber +- ); +- +- return reg0; +- +-#elif defined(CONFIG_PPC) +- register unsigned long reg0 asm("r0") = num; +- register unsigned long reg1 asm("r3") = arg1; +- +- asm volatile( +- "xori 10, 10, 0" // User-specified instruction - ASSUMED CORRECT TRIGGER +- : "+r"(reg0) // Input num in r0, Output retval in r0 +- : "r"(reg1) // Input arg1 in r3 +- : "memory", "lr", "ctr", // CRITICAL: clobber link and count registers +- // Clobber volatile condition register fields: +- "cr0", "cr1", "cr5", "cr6", "cr7", +- // Clobber volatile GPRs (r4-r12) - r0,r3 handled by constraints: +- "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" +- // Note: r2 (TOC) and r13 (thread ptr) are usually non-volatile but check hypervisor docs +- ); +- +- return reg0; +- +-#elif defined(CONFIG_RISCV) +- register unsigned long reg0 asm("a7") = num; +- register unsigned long reg1 asm("a0") = arg1; ++#include + +- asm volatile( +- "xori x0, x0, 0" +- : "+r"(reg1) /* Modified: a0/reg1 is both input and output */ +- : "r"(reg0) +- : "memory" +- ); +- +- return reg1; +- +-#else +-#error "No igloo_hypercall support for architecture" +-#endif +-} +- +-static inline unsigned long igloo_hypercall2(unsigned long num, unsigned long arg1, unsigned long arg2) { +-#if defined(CONFIG_ARM64) +- register unsigned long reg0 asm("x8") = num; +- register unsigned long reg1 asm("x0") = arg1; +- register unsigned long reg2 asm("x1") = arg2; +- asm volatile( +- "msr S0_0_c5_c0_0, xzr \n" +- : "+r"(reg1) // Input and output +- : "r"(reg0), "r"(reg2) +- : "memory" +- ); +- return reg1; +-#elif defined(CONFIG_ARM) +- register unsigned long reg0 asm("r7") = num; +- register unsigned long reg1 asm("r0") = arg1; +- register unsigned long reg2 asm("r1") = arg2; +- +- asm volatile( +- "mcr p7, 0, r0, c0, c0, 0" +- : "+r"(reg1) // Input and output +- : "r"(reg0), "r"(reg2) +- : "memory" +- ); +- +- return reg1; +- +-#elif defined(CONFIG_MIPS) +- register unsigned long reg0 asm("v0") = num; +- register unsigned long reg1 asm("a0") = arg1; +- register unsigned long reg2 asm("a1") = arg2; +- +- asm volatile( +- "movz $0, $0, $0" +- : "+r"(reg0) // Input and output in v0 +- : "r"(reg1), "r"(reg2) +- : "memory" +- ); +- return reg0; +-#elif defined(CONFIG_X86_64) +- register unsigned long reg0 asm("rax") = num; +- register unsigned long reg1 asm("rdi") = arg1; +- register unsigned long reg2 asm("rsi") = arg2; +- +- asm volatile( +- "cpuid" +- : "+r"(reg0) // hypercall num + return value in rax +- : "r"(reg1), "r"(reg2) // arguments +- : "memory", "rbx", "rcx", "rdx" +- ); +- +- return reg0; +-#elif defined(CONFIG_I386) +- register unsigned long reg0 asm("eax") = num; +- register unsigned long reg1 asm("edi") = arg1; +- register unsigned long reg2 asm("esi") = arg2; +- +- asm volatile( +- "cpuid" +- : "+r"(reg0) // hypercall num + return value in rax +- : "r"(reg1), "r"(reg2) // arguments +- : "memory", "ebx", "ecx", "edx" +- ); +- +- return reg0; +-#elif defined(CONFIG_LOONGARCH) +- register unsigned long reg0 asm("a7") = num; +- register unsigned long reg1 asm("a0") = arg1; +- register unsigned long reg2 asm("a1") = arg2; +- +- asm volatile( +- "cpucfg $r0, $r0" +- : "+r"(reg1) /* a0/reg1 is both input and output */ +- : "r"(reg0), "r"(reg2) +- : "memory" +- ); +- return reg1; /* Return reg1 (a0) which contains the return value from the hypervisor */ +-#elif defined(CONFIG_PPC) || defined(CONFIG_PPC64) +- register unsigned long reg0 asm("r0") = num; +- register unsigned long reg1 asm("r3") = arg1; +- register unsigned long reg2 asm("r4") = arg2; // Second arg in r4 +- +- asm volatile( +- "xori 10, 10, 0" // User-specified instruction +- : "+r"(reg1) // Input and output in r3 +- : "r"(reg0), "r"(reg2) +- : "memory", "lr", "ctr", +- "cr0", "cr1", "cr5", "cr6", "cr7", +- "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" +- ); +- return reg1; /* Return reg1 (r3) which contains the return value from the hypervisor */ +-#elif defined(CONFIG_RISCV) +- register unsigned long reg0 asm("a7") = num; +- register unsigned long reg1 asm("a0") = arg1; +- register unsigned long reg2 asm("a1") = arg2; +- +- asm volatile( +- "xori x0, x0, 0" +- : "+r"(reg1) /* a0/reg1 is both input and output */ +- : "r"(reg0), "r"(reg2) +- : "memory" +- ); +- return reg1; /* Return reg1 (a0) which contains the return value from the hypervisor */ +-#else +-#error "No igloo_hypercall2 support for architecture" +-#endif +-} +- +-static inline unsigned long igloo_hypercall3(unsigned long num, unsigned long arg1, unsigned long arg2, unsigned long arg3) { ++static inline unsigned long igloo_hypercall4(unsigned long num, ++ unsigned long arg1, ++ unsigned long arg2, ++ unsigned long arg3, ++ unsigned long arg4) ++{ + #if defined(CONFIG_ARM64) + register unsigned long reg0 asm("x8") = num; + register unsigned long reg1 asm("x0") = arg1; + register unsigned long reg2 asm("x1") = arg2; + register unsigned long reg3 asm("x2") = arg3; ++ register unsigned long reg4 asm("x3") = arg4; ++ + asm volatile( +- "msr S0_0_c5_c0_0, xzr \n" +- : "+r"(reg1) // Input and output +- : "r"(reg0), "r"(reg2), "r"(reg3) ++ "msr S0_0_c5_c0_0, xzr \n" ++ : "+r"(reg1) ++ : "r"(reg0), "r"(reg2), "r"(reg3), "r"(reg4) + : "memory" + ); + return reg1; +@@ -241,101 +28,118 @@ static inline unsigned long igloo_hypercall3(unsigned long num, unsigned long ar + register unsigned long reg1 asm("r0") = arg1; + register unsigned long reg2 asm("r1") = arg2; + register unsigned long reg3 asm("r2") = arg3; ++ register unsigned long reg4 asm("r3") = arg4; + + asm volatile( +- "mcr p7, 0, r0, c0, c0, 0" +- : "+r"(reg1) // Input and output +- : "r"(reg0), "r"(reg2), "r"(reg3) ++ "mcr p7, 0, r0, c0, c0, 0" ++ : "+r"(reg1) ++ : "r"(reg0), "r"(reg2), "r"(reg3), "r"(reg4) + : "memory" + ); +- + return reg1; +- + #elif defined(CONFIG_MIPS) + register unsigned long reg0 asm("v0") = num; + register unsigned long reg1 asm("a0") = arg1; + register unsigned long reg2 asm("a1") = arg2; + register unsigned long reg3 asm("a2") = arg3; ++ register unsigned long reg4 asm("a3") = arg4; + + asm volatile( +- "movz $0, $0, $0" +- : "+r"(reg0) // Input and output in v0 +- : "r"(reg1), "r"(reg2), "r"(reg3) ++ "movz $0, $0, $0" ++ : "+r"(reg0) ++ : "r"(reg1), "r"(reg2), "r"(reg3), "r"(reg4) + : "memory" + ); + return reg0; + #elif defined(CONFIG_X86_64) +- register unsigned long reg0 asm("rax") = num; +- register unsigned long reg1 asm("rdi") = arg1; +- register unsigned long reg2 asm("rsi") = arg2; +- register unsigned long reg3 asm("rdx") = arg3; ++ unsigned long reg0 = num; ++ register unsigned long reg4 asm("r10") = arg4; + + asm volatile( +- "cpuid" +- : "+r"(reg0) // hypercall num + return value in rax +- : "r"(reg1), "r"(reg2), "r"(reg3) // arguments +- : "memory", "rbx", "rcx" ++ "outl %%eax, $0x88" ++ : "+a"(reg0) ++ : "D"(arg1), "S"(arg2), "d"(arg3), "r"(reg4) ++ : "memory" + ); +- + return reg0; + #elif defined(CONFIG_I386) +- register unsigned long reg0 asm("eax") = num; +- register unsigned long reg1 asm("edi") = arg1; +- register unsigned long reg2 asm("esi") = arg2; +- register unsigned long reg3 asm("edx") = arg3; ++ unsigned long reg0 = num; + + asm volatile( +- "cpuid" +- : "+r"(reg0) // hypercall num + return value in eax +- : "r"(reg1), "r"(reg2), "r"(reg3) // arguments +- : "memory", "ebx", "ecx" ++ "outl %%eax, $0x88" ++ : "+a"(reg0) ++ : "b"(arg1), "c"(arg2), "d"(arg3), "S"(arg4) ++ : "memory" + ); +- + return reg0; + #elif defined(CONFIG_LOONGARCH) + register unsigned long reg0 asm("a7") = num; + register unsigned long reg1 asm("a0") = arg1; + register unsigned long reg2 asm("a1") = arg2; + register unsigned long reg3 asm("a2") = arg3; ++ register unsigned long reg4 asm("a3") = arg4; + + asm volatile( + "cpucfg $r0, $r0" +- : "+r"(reg1) /* a0/reg1 is both input and output */ +- : "r"(reg0), "r"(reg2), "r"(reg3) ++ : "+r"(reg1) ++ : "r"(reg0), "r"(reg2), "r"(reg3), "r"(reg4) + : "memory" + ); +- return reg1; /* Return reg1 (a0) which contains the return value from the hypervisor */ ++ return reg1; + #elif defined(CONFIG_PPC) || defined(CONFIG_PPC64) + register unsigned long reg0 asm("r0") = num; + register unsigned long reg1 asm("r3") = arg1; + register unsigned long reg2 asm("r4") = arg2; + register unsigned long reg3 asm("r5") = arg3; +- ++ register unsigned long reg4 asm("r6") = arg4; ++ + asm volatile( +- "xori 10, 10, 0" // User-specified instruction +- : "+r"(reg1) // Input and output in r3 +- : "r"(reg0), "r"(reg2), "r"(reg3) ++ "xori 10, 10, 0" ++ : "+r"(reg1) ++ : "r"(reg0), "r"(reg2), "r"(reg3), "r"(reg4) + : "memory", "lr", "ctr", + "cr0", "cr1", "cr5", "cr6", "cr7", +- "r6", "r7", "r8", "r9", "r10", "r11", "r12" ++ "r7", "r8", "r9", "r10", "r11", "r12" + ); +- return reg1; /* Return reg1 (r3) which contains the return value from the hypervisor */ ++ return reg1; + #elif defined(CONFIG_RISCV) + register unsigned long reg0 asm("a7") = num; + register unsigned long reg1 asm("a0") = arg1; + register unsigned long reg2 asm("a1") = arg2; + register unsigned long reg3 asm("a2") = arg3; ++ register unsigned long reg4 asm("a3") = arg4; + + asm volatile( + "xori x0, x0, 0" +- : "+r"(reg1) /* a0/reg1 is both input and output */ +- : "r"(reg0), "r"(reg2), "r"(reg3) ++ : "+r"(reg1) ++ : "r"(reg0), "r"(reg2), "r"(reg3), "r"(reg4) + : "memory" + ); +- return reg1; /* Return reg1 (a0) which contains the return value from the hypervisor */ ++ return reg1; + #else +-#error "No igloo_hypercall3 support for architecture" ++#error "No igloo_hypercall4 support for architecture" + #endif + } + +-#endif +\ No newline at end of file ++static inline unsigned long igloo_hypercall(unsigned long num, ++ unsigned long arg1) ++{ ++ return igloo_hypercall4(num, arg1, 0, 0, 0); ++} ++ ++static inline unsigned long igloo_hypercall2(unsigned long num, ++ unsigned long arg1, ++ unsigned long arg2) ++{ ++ return igloo_hypercall4(num, arg1, arg2, 0, 0); ++} ++ ++static inline unsigned long igloo_hypercall3(unsigned long num, ++ unsigned long arg1, ++ unsigned long arg2, ++ unsigned long arg3) ++{ ++ return igloo_hypercall4(num, arg1, arg2, arg3, 0); ++} ++ ++#endif diff --git a/scripts/export-series.sh b/scripts/export-series.sh new file mode 100755 index 0000000..71472ee --- /dev/null +++ b/scripts/export-series.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Write a development tree back out as the committed patch series. +# +# ./scripts/export-series.sh 6.13 /path/to/workdir +# +# Regenerates patches//*.patch and the series file from every commit +# above base-. Patches whose content is byte-identical to an existing +# core/ patch keep referencing core/ rather than being duplicated per version. +set -euo pipefail + +VERSION="${1:?usage: export-series.sh }" +WORKDIR="${2:?}" + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PATCHES="$REPO_ROOT/patches" +BASE_TAG=$(python3 -c "import json;print(json.load(open('$PATCHES/base.json'))['$VERSION']['tag'])") + +STAGE=$(mktemp -d); trap 'rm -rf "$STAGE"' EXIT +git -C "$WORKDIR" format-patch --no-signature -o "$STAGE" "base-${BASE_TAG}..HEAD" >/dev/null + +norm() { sed -e '/^From [0-9a-f]\{40\}/d' -e '/^index [0-9a-f]/d' -e '/^From: /d' -e '/^Date: /d' "$1"; } + +rm -f "$PATCHES/$VERSION"/*.patch +: > "$PATCHES/$VERSION/series" + +for f in "$STAGE"/*.patch; do + n=$(basename "$f") + matched="" + for c in "$PATCHES"/core/*.patch; do + [ -e "$c" ] || continue + if diff -q <(norm "$f") <(norm "$c") >/dev/null 2>&1; then matched="core/$(basename "$c")"; break; fi + done + if [ -n "$matched" ]; then + echo "$matched" >> "$PATCHES/$VERSION/series" + else + cp "$f" "$PATCHES/$VERSION/$n" + echo "$VERSION/$n" >> "$PATCHES/$VERSION/series" + fi +done + +echo "OK: exported $(wc -l < "$PATCHES/$VERSION/series") patches for $VERSION" +echo " verify with: ./scripts/verify-series.sh $VERSION " diff --git a/scripts/import-series.sh b/scripts/import-series.sh new file mode 100755 index 0000000..353c626 --- /dev/null +++ b/scripts/import-series.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Git-as-workspace: materialise a version's series as a real git tree you can +# develop in, bisect within, and rebase. +# +# ./scripts/import-series.sh 6.13 /path/to/workdir +# +# Develop there as normal (commit on top), then run export-series.sh to write +# the series back. The committed patches remain the source of truth; this tree +# is a regenerated convenience, not a long-lived branch -- which is the whole +# point of the migration away from main_4.10 / main_6.13. +set -euo pipefail + +VERSION="${1:?usage: import-series.sh }" +WORKDIR="${2:?}" + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PATCHES="$REPO_ROOT/patches" + +[ -f "$PATCHES/$VERSION/series" ] || { echo "no series for $VERSION" >&2; exit 1; } + +BASE_TAG=$(python3 -c "import json;print(json.load(open('$PATCHES/base.json'))['$VERSION']['tag'])") +MAJOR="${BASE_TAG%%.*}" +URL="https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${BASE_TAG}.tar.xz" + +mkdir -p "$WORKDIR" +if [ ! -e "$WORKDIR/Makefile" ]; then + echo ">>> fetching $URL" + TARBALL=$(nix-prefetch-url --print-path "$URL" 2>/dev/null | tail -1) + echo ">>> extracting into $WORKDIR" + tar xf "$TARBALL" -C "$WORKDIR" --strip-components=1 + git -C "$WORKDIR" init -q . + git -C "$WORKDIR" add -A + git -C "$WORKDIR" -c user.email=igloo@local -c user.name=igloo \ + commit -qm "linux ${BASE_TAG} (pristine upstream tarball)" + git -C "$WORKDIR" tag "base-${BASE_TAG}" +fi + +echo ">>> applying series" +while read -r p; do + case "$p" in ''|\#*) continue ;; esac + git -C "$WORKDIR" -c user.email=igloo@local -c user.name=igloo \ + am -q "$PATCHES/$p" +done < "$PATCHES/$VERSION/series" + +echo "OK: $WORKDIR is linux ${BASE_TAG} + the IGLOO ${VERSION} series" +echo " base tag: base-${BASE_TAG} (export with: export-series.sh $VERSION $WORKDIR)" diff --git a/scripts/verify-series.sh b/scripts/verify-series.sh new file mode 100755 index 0000000..86c2d6d --- /dev/null +++ b/scripts/verify-series.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# Slice 1 acceptance test. +# +# Applies patches//series to the PRISTINE kernel.org tarball and proves +# the result matches the fork branch we are replacing. +# +# ./scripts/verify-series.sh 4.10 +# +# NOTE ON THE BASELINE. kernel.org release tarballs are `git archive` output and +# therefore honour .gitattributes `export-ignore`, so they are NOT byte-identical +# to the git tag: v4.10's tarball lacks .gitattributes, .get_maintainer.ignore, +# and two arch/sh linker scripts. None are build-affecting and none are touched +# by the IGLOO series. +# +# So the test is not "tree hashes are equal" (they can't be) but the stronger, +# checkable claim: the patched tree differs from the fork branch ONLY by that +# known export-ignore set. Any other path appearing in the diff is a real +# regression and fails the run. +set -euo pipefail + +VERSION="${1:?usage: verify-series.sh [ ]}" +TARBALL="${2:?}" +# Optional: compare against the fork branch being replaced. This is a MIGRATION +# check -- once the branches are retired there is nothing to compare to, and the +# permanent invariant CI enforces is simply "the series applies cleanly". +FORK_REF="${3:-}" +GIT_DIR="${4:-}" + +# Paths upstream marks export-ignore, so they are absent from release tarballs. +# Anything else in the final diff is a genuine mismatch. +EXPORT_IGNORED='^(\.gitattributes|\.get_maintainer\.ignore|arch/sh/boot/(compressed|romimage)/vmlinux\.scr)$' + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PATCHES="$REPO_ROOT/patches" +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +echo ">>> extracting $(basename "$TARBALL")" +mkdir -p "$WORK/src" +tar xf "$TARBALL" -C "$WORK/src" --strip-components=1 + +cd "$WORK/src" +git init -q . +git add -A +git -c user.email=nix@local -c user.name=nix commit -qm "pristine upstream" +BASE_TREE=$(git rev-parse HEAD^{tree}) +echo ">>> pristine tree: $BASE_TREE" + +echo ">>> applying $(grep -cv '^\s*\(#.*\)\?$' "$PATCHES/$VERSION/series") patches" +n=0 +while read -r p; do + case "$p" in ''|\#*) continue ;; esac + n=$((n + 1)) + if ! git -c user.email=nix@local -c user.name=nix am -q "$PATCHES/$p" 2>/dev/null; then + echo "FAIL: patch $n did not apply: $p" >&2 + git am --abort 2>/dev/null || true + exit 1 + fi +done < "$PATCHES/$VERSION/series" + +echo ">>> patched tree: $(git rev-parse HEAD^{tree})" + +if [ -z "$FORK_REF" ]; then + echo "PASS: $n patches applied cleanly to pristine linux-${VERSION}" + echo " (no fork ref given; skipping migration comparison)" + exit 0 +fi + +git remote add fork "$GIT_DIR" +git fetch -q --depth 60 fork "+${FORK_REF}:refs/heads/forkref" + +UNEXPECTED=$(git diff --name-only forkref HEAD | grep -Ev "$EXPORT_IGNORED" || true) + +if [ -z "$UNEXPECTED" ]; then + echo "PASS: $n patches applied cleanly; tree matches $FORK_REF" + echo " (differs only by upstream export-ignored paths, as expected)" +else + echo "MISMATCH: unexpected differences vs $FORK_REF:" >&2 + echo "$UNEXPECTED" >&2 + exit 1 +fi