Create your own
Lesson illustration

Reproducing and Validating the V1 Kernel Patch Series

Hello. This course starts by making the V1 driver series a controlled baseline. Before an AI agent edits anything, you need to know exactly what was submitted, exactly what kernel it targeted, and exactly what your local environment reported when you built and checked it.

This first module establishes that baseline. The outcome is not “make V1 pass.” A warning, build error, or DT schema failure may be an important fact about V1. Record it faithfully; do not repair it yet. Later, every proposed fix and reviewer response should be comparable to this starting point.

By the end of this lesson, you will have:

  • the V1 series retrieved as V1, not silently replaced by a later revision;
  • an untouched restoration on its intended kernel base;
  • separate editable and baseline work areas;
  • a machine-readable record of environment, commands, logs, exit codes, and results.

Treat V1 as evidence, not as a working branch

A reproducible V1 baseline has four distinct identities:

IdentityWhat to recordWhy it matters
Submission identityV1 cover-letter or patch Message-ID, selected revision number, mailbox checksumProves which submission you reviewed
Kernel-base identityRemote URL, exact base commit SHA, tag or git describe outputPrevents accidental testing on a nearby but different kernel
Restored-series identityOrdered restored commit list and restored tip SHAShows what was actually applied locally
Environment identityCompiler, Git, b4, dtc, sparse, Python, host details, configuration checksumMakes build and validation results interpretable

A kernel release tag alone is often not enough. A series may have been based on a particular commit in a subsystem tree, a linux-next snapshot, or a point between tags. The desired base is the precise commit stated in the V1 material, not the newest tree that happens to accept the series.

Use a directory layout that keeps inputs, baseline code, editable work, and generated output apart:

driver-project/
├── artifacts/
│   ├── v1-mail/
│   ├── manifests/
│   └── bundles/
├── logs/
├── out/
│   ├── baseline/
│   ├── warnings/
│   ├── sparse/
│   └── dt/
├── linux.git/                  # shared Git repository
├── linux-v1-baseline/          # never edit here
└── linux-v1-work/              # later repair work happens here

The key rule is simple:

No source edit, conflict resolution, formatting, trailer addition, or generated build output belongs in linux-v1-baseline/.

Using out-of-tree builds through O=... helps enforce this rule. At the end of every baseline run, git status --porcelain=v1 in the baseline worktree should be empty.


Retrieve the actual V1 series

The kernel mailing-list workflow matters here. A thread can contain V1, V2, review replies, automated reports, and follow-up trailers. If you simply ask a tool for “the series,” it may select the highest version or incorporate later review metadata. That is useful in some workflows, but it is wrong for restoring the untouched V1 starting point.

am,shazam: retrieving and applying patches

Read the b4 documentation to understand why retrieving a thread is different from modifying your branch, and why explicit version selection is necessary.

In “am, shazam: retrieving and applying patches,” begin with the retrieval workflow. Notice that b4 retrieves the whole thread but prepares an ordered mailbox for Git. Then, in the “Common flags” section, read version selection and the no-trailers option. For this baseline, you want V1 explicitly and you do not want later follow-up trailers inserted into the restored commits.

Start by recording the source identifier. Prefer the V1 cover-letter Message-ID. If there is no cover letter, use the Message-ID of patch 1 and record that limitation in the manifest.

A practical retrieval pattern is:

export V1_MSGID='<V1-cover-letter-or-patch-1-message-id>'
export ARTIFACTS="$PWD/artifacts"

mkdir -p "$ARTIFACTS/v1-mail"

b4 --version | tee "$ARTIFACTS/b4-version.txt"
b4 am --help > "$ARTIFACTS/b4-am-help.txt"

(
    cd "$ARTIFACTS/v1-mail"
    b4 am -v 1 -T "$V1_MSGID"
)

find "$ARTIFACTS/v1-mail" -maxdepth 1 -type f -print
sha256sum "$ARTIFACTS/v1-mail"/* > "$ARTIFACTS/v1-mail/SHA256SUMS"

The exact output filename is b4-version dependent, so discover and record it rather than assuming a fixed name. Your manifest should name the selected mailbox file and its SHA-256 digest.

The important options are:

  • -v 1: explicitly choose V1 rather than b4’s normal choice of the newest revision.
  • -T: do not add review trailers from later emails.
  • no -s, -l, or -i: do not add your own signoff, link, or Message-ID trailer to the baseline commits.

The generated mailbox is your preserved input artifact. Keep it even after Git has applied it successfully.

Establish the exact kernel base

Inspect the V1 cover letter and patches for a base-commit: line, an explicit tree and commit reference, or a clearly stated kernel revision. Record the evidence text and the exact resolved commit SHA.

Do not choose a base merely because the patches apply there. Clean application is a useful confirmation, not proof of author intent.

If the V1 material identifies only a release name such as “based on v6.x,” but gives no exact commit or tree, mark the base as unconfirmed and stop before creating an “exact” baseline. That is a focused human question:

Which repository and exact commit was used to create V1?

Once the base is known, obtain the matching tree and create an isolated baseline worktree:

export KERNEL_REMOTE='<kernel-tree-url>'
export BASE='<40-character-base-commit-sha>'

git clone --origin upstream "$KERNEL_REMOTE" linux.git
git -C linux.git fetch --tags upstream

git -C linux.git cat-file -e "$BASE^{commit}"
git -C linux.git show -s --format=fuller "$BASE"
git -C linux.git describe --always --tags "$BASE"

git -C linux.git worktree add -b v1-restore \
    ../linux-v1-baseline "$BASE"

Save the output of the show and describe commands. The SHA is authoritative; the tag or description is human-readable context.


Apply without silently repairing the series

git am converts mail messages into commits. It uses email metadata for authorship and subject, which is exactly what you want when restoring a mailing-list patch series. It will create new local commit objects, however, so do not expect their commit hashes to match hashes from the author’s private branch.

Git - git-am Documentation

Read the core behavior of git am and its handling of email metadata. This explains both why it is appropriate for a mailed V1 series and why a failed application must not be hand-fixed in the baseline.

In the “DESCRIPTION” section, read the core purpose of git am. Then read the opening of the “DISCUSSION” section, from metadata handling. Finally, read the failure behavior. For this lesson, conflict resolution and skipping are diagnostic tools, not baseline-restoration actions.

Apply the retrieved mailbox to the baseline worktree using normal patch application, without a three-way merge:

export V1_TREE="$PWD/../linux-v1-baseline"
export V1_MBOX="$(find "$ARTIFACTS/v1-mail" -maxdepth 1 -type f | head -n 1)"

git -C "$V1_TREE" am --no-3way "$V1_MBOX"

Why explicitly avoid three-way application here?

  • A clean direct application supports the claim that this is the intended base.
  • A three-way merge may hide a mismatch by reconstructing a merge result.
  • Manual conflict resolution changes V1. It belongs in a later porting or repair branch, not in the evidence baseline.
  • git am --skip silently removes a patch and is never valid for a complete V1 restoration.

If application fails, capture the output, run:

git -C "$V1_TREE" am --abort

Then record the failure as RESTORE_BLOCKED. Investigate whether the Message-ID, V1 selection, kernel remote, or base SHA is wrong. Do not “make it work” by editing files.

If application succeeds, capture the restored series identity:

git -C "$V1_TREE" status --porcelain=v1
git -C "$V1_TREE" rev-list --reverse "$BASE..HEAD"
git -C "$V1_TREE" log --reverse \
    --format='%H%n%an <%ae>%n%aI%n%s%n' "$BASE..HEAD"
git -C "$V1_TREE" diff --check "$BASE..HEAD"

Then freeze it in more than one way:

git -C "$V1_TREE" tag -a baseline/v1-restored \
    -m "Untouched V1 restored on recorded base"

git -C "$V1_TREE" bundle create \
    "$ARTIFACTS/bundles/v1-restored.bundle" \
    "$BASE..baseline/v1-restored"

sha256sum "$ARTIFACTS/bundles/v1-restored.bundle" \
    > "$ARTIFACTS/bundles/SHA256SUMS"

The bundle contains the restored series and records the base as a prerequisite; preserve the base SHA in the manifest. The original V1 mailbox remains the primary evidence of what was sent. The annotated tag and bundle protect your local reconstruction.

Create the worktree that later lessons and AI agents may modify:

git -C linux.git worktree add -b v1-work \
    ../linux-v1-work baseline/v1-restored

From now on, linux-v1-baseline is for rebuilding and rechecking the untouched state. linux-v1-work is for analysis and future repairs.


Record commands and results as an audit trail

A terminal scrollback is not evidence. It disappears, is hard to compare, and does not reliably preserve exit codes. Each command should produce:

  1. a command record;
  2. start and end times in UTC;
  3. working directory;
  4. complete stdout and stderr log;
  5. exit code;
  6. a short result classification;
  7. a checksum of the log.

A small runner is sufficient for the baseline. This is intentionally narrower than the full agent loop you will build later.

#!/usr/bin/env bash
# baseline-runner.sh
set -u

LOGDIR="${LOGDIR:?Set LOGDIR}"
mkdir -p "$LOGDIR"

run_check() {
    local name="$1"
    shift

    local started ended rc
    local log="$LOGDIR/${name}.log"
    local meta="$LOGDIR/${name}.meta"

    started="$(date -u +%FT%TZ)"
    {
        printf 'name: %s\n' "$name"
        printf 'started_utc: %s\n' "$started"
        printf 'cwd: %s\n' "$PWD"
        printf 'command: '
        printf '%q ' "$@"
        printf '\n'
    } > "$meta"

    "$@" > "$log" 2>&1
    rc="$?"

    ended="$(date -u +%FT%TZ)"
    {
        printf 'ended_utc: %s\n' "$ended"
        printf 'exit_code: %s\n' "$rc"
        sha256sum "$log"
    } >> "$meta"

    return 0
}

The runner returns success so that one failed check does not prevent later baseline checks from running. The real result is the recorded exit code, not the runner’s exit status.

Capture the main toolchain facts before building:

run_check git-version git --version
run_check b4-version b4 --version
run_check make-version make --version
run_check compiler-version "${CC:-gcc}" --version
run_check python-version python3 --version
run_check dtc-version dtc --version
run_check sparse-version sparse --version
run_check host uname -a

Also save the relevant build environment in a text file: ARCH, CROSS_COMPILE, CC, LLVM, LLVM_IAS, PATH, and any project-specific build variables. If a cross compiler is used, record its resolved path with command -v.

Baseline validation matrix

Use the configuration and architecture actually relevant to the V1 driver. A default configuration is not meaningful if it does not enable the driver, DSA, the bus support, or the DT platform path exercised by the series.

Keep build outputs separate so that incremental artifacts cannot hide a warning or skipped compilation.

Check classTypical command shapeWhat the record must establish
Configurationmake O=<out> ARCH=<arch> <defconfig> followed by olddefconfigWhich configuration enabled the driver and its dependencies
Normal buildmake O=<out> ARCH=<arch> -j<n>Whether the configured kernel builds
Warning buildmake O=<out> ARCH=<arch> W=1 -j<n>Compiler warnings under the stated warning level
Kernel static checkmake O=<out> ARCH=<arch> C=2 -j<n>Sparse findings, if sparse is installed and the relevant objects compile
Patch style checkscripts/checkpatch.pl --strict <V1-mailbox>Style and commit-message findings against the original mailed series
DT binding checkmake O=<out> ARCH=<arch> dt_binding_check DT_SCHEMA_FILES=<binding>Whether the changed binding validates
DTB schema checkmake O=<out> ARCH=<arch> dtbs_check DT_SCHEMA_FILES=<binding>Whether the relevant built DTBs satisfy the binding schema

For V1, derive <binding> and the candidate DTB target from the patch range, not from assumptions:

git -C "$V1_TREE" diff --name-status "$BASE..baseline/v1-restored" \
    -- Documentation/devicetree/bindings arch

If V1 does not contain a binding or board DTS change, record the corresponding DT check as NOT_APPLICABLE, with the reason. Do not invent a board file merely to make a check appear complete.

A representative set of recorded invocations might look like this:

export ARCH=arm64
export DEFCONFIG=defconfig
export OUTROOT="$PWD/out"
export BINDING='Documentation/devicetree/bindings/net/dsa/<vendor>,<switch>.yaml'

run_check baseline-config \
    make -C "$V1_TREE" O="$OUTROOT/baseline" ARCH="$ARCH" "$DEFCONFIG"

run_check baseline-olddefconfig \
    make -C "$V1_TREE" O="$OUTROOT/baseline" ARCH="$ARCH" olddefconfig

run_check baseline-build \
    make -C "$V1_TREE" O="$OUTROOT/baseline" ARCH="$ARCH" -j"$(nproc)"

run_check warning-build \
    make -C "$V1_TREE" O="$OUTROOT/warnings" ARCH="$ARCH" W=1 -j"$(nproc)"

run_check sparse-build \
    make -C "$V1_TREE" O="$OUTROOT/sparse" ARCH="$ARCH" C=2 -j"$(nproc)"

run_check checkpatch-v1 \
    "$V1_TREE/scripts/checkpatch.pl" --strict "$V1_MBOX"

run_check dt-binding \
    make -C "$V1_TREE" O="$OUTROOT/dt" ARCH="$ARCH" \
    dt_binding_check DT_SCHEMA_FILES="$BINDING"

run_check dt-schema \
    make -C "$V1_TREE" O="$OUTROOT/dt" ARCH="$ARCH" \
    dtbs_check DT_SCHEMA_FILES="$BINDING"

Replace the placeholders only with values supported by the V1 diff, the intended platform configuration, or explicit project requirements. Record the replacement source in the manifest.

The Device Tree checks deserve special care. A successful YAML binding check does not prove a board DTB is valid, and a valid DTB does not prove the binding schema is adequate. Keep those results separate.


Make failures useful rather than ambiguous

Use result labels that distinguish facts from decisions:

StatusMeaning
PASSCommand completed with the expected result
BASELINE_FAILCommand failed on untouched V1
WARNINGS_RECORDEDCommand completed, but warnings need later review
NOT_APPLICABLEThe series does not contain the relevant artifact
BLOCKEDRequired input, tool, configuration, or base is unavailable
RESTORE_BLOCKEDV1 could not be restored faithfully

A baseline failure is not an instruction to modify the code. It becomes input to the later work list. For example:

  • checkpatch reports a line-length issue: record the exact log location.
  • Sparse reports an endian or type warning: record it, but do not infer the intended hardware behavior.
  • DT schema validation fails: preserve both the binding and DTB logs, because either side may be wrong.
  • The driver object was never built because its Kconfig symbol is disabled: mark the build coverage gap instead of calling the build clean.

A compact manifest can tie all artifacts together:

baseline:
  v1_message_id: "<message-id>"
  selected_version: 1
  source_mbox: "artifacts/v1-mail/<file>"
  source_mbox_sha256: "<digest>"

kernel:
  remote: "<url>"
  base_commit: "<full-sha>"
  base_description: "<git-describe-output>"
  restored_tip: "<full-sha>"
  restored_tag: "baseline/v1-restored"

environment:
  arch: "<arch>"
  compiler: "<path and version>"
  git: "<version>"
  b4: "<version>"
  dtc: "<version>"
  sparse: "<version or unavailable>"
  config_sha256: "<digest>"

checks:
  - id: baseline-build
    command_meta: "logs/baseline-build.meta"
    log: "logs/baseline-build.log"
    exit_code: 0
    status: PASS
  - id: dt-schema
    command_meta: "logs/dt-schema.meta"
    log: "logs/dt-schema.log"
    exit_code: 2
    status: BASELINE_FAIL
    note: "Untouched V1 result; no fix attempted."

Finally, verify the freeze:

git -C "$V1_TREE" status --porcelain=v1
git -C "$V1_TREE" rev-parse baseline/v1-restored
sha256sum -c "$ARTIFACTS/v1-mail/SHA256SUMS"
sha256sum -c "$ARTIFACTS/bundles/SHA256SUMS"

An empty status output is required. Any source change in the baseline worktree invalidates the run and requires restoration from the preserved mailbox and base.


Key takeaways

A trustworthy V1 baseline is a reproducibility asset, not a clean-build claim.

  • Retrieve V1 explicitly and preserve the mailbox checksum.
  • Use the exact stated base commit; do not infer it from a patch applying successfully.
  • Apply without three-way merge, skipping, or manual conflict resolution.
  • Preserve both the original mailbox and a tagged, bundled local restoration.
  • Build and validate from the untouched baseline using separate output directories.
  • Save commands, versions, logs, exit codes, and status labels, including failures.
  • Never convert a V1 failure into a code fix during baseline capture.

Next, you will turn the upstream review thread into a work list that retains each reviewer’s original wording and context while making every comment testable and traceable.

Can't find a good explanation? Sign up and we'll make it for you

Sign up