Patching Workflow
About 1350 wordsAbout 5 min
2026-03-23
Why Patching Matters in EDK2
EDK2 platforms are rarely built from upstream edk2 HEAD alone. Vendor platforms and product firmware typically maintain a patch series on top of a pinned upstream commit. This is because:
- Upstream
edk2evolves independently of product release schedules - Vendor-specific hardware quirks require changes to generic modules
- Security CVEs require backporting fixes without pulling in unrelated upstream changes
- Architectural review takes time; features needed for a product ship window exist as patches before upstream acceptance
Understanding how to create, maintain, and update patch series is a core EDK2 engineering skill.
EDK2 Repository Setup for Patching
EDK2 uses git submodules extensively. At minimum:
# Clone with submodules
git clone --recurse-submodules https://github.com/tianocore/edk2.git
cd edk2
# Or initialize submodules in an existing clone
git submodule update --init --recursive
# Critical submodules:
# - CryptoPkg/Library/OpensslLib/openssl (OpenSSL for CryptoPkg)
# - MdeModulePkg/Library/BrotliCustomDecompressLib/brotli
# - BaseTools/Source/C/BrotliCompress/brotli
# - UnitTestFrameworkPkg/Library/CmockaLib/cmocka
# - RedfishPkg/ dependencies (multiple)Working with Platform Repos
A typical platform repository structure:
my-platform-firmware/
├── edk2/ ← EDK2 upstream (git submodule, pinned to a commit)
├── edk2-platforms/ ← Community platform packages (git submodule)
├── edk2-non-osi/ ← Non-OSI blobs (git submodule)
├── patches/
│ ├── edk2/ ← Patch series for edk2 submodule
│ │ ├── 0001-MdePkg-Fix-PcdLib-null-pointer.patch
│ │ ├── 0002-ArmPkg-Add-MySoC-GIC-quirk.patch
│ │ └── series ← (optional quilt series file)
│ └── edk2-platforms/
│ └── 0001-Add-MySoC-board-support.patch
├── MySoCPkg/ ← Proprietary platform package (in-tree)
└── Makefile ← Top-level build orchestrationCreating Patches with git format-patch
Single Patch
After making changes to edk2/ and committing:
cd edk2/
# Make your change
vim MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
# Commit with EDK2 code review style
git add MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c
git commit -s -m "MdeModulePkg/Variable: Fix integer overflow in GetVariable
The DataSize parameter is UINTN but the internal calculation
DataSize + sizeof(Header) can overflow on 32-bit builds.
Add overflow check before the addition.
Cc: Someone Reviewer <someone@example.com>
Reviewed-by: (pending)
Signed-off-by: Your Name <you@company.com>"
# Create a patch file
git format-patch -1 HEAD
# Outputs: 0001-MdeModulePkg-Variable-Fix-integer-overflow-in-GetVar.patchPatch Series (Multiple Commits)
# Format a range of commits as a numbered series
git format-patch origin/main..HEAD
# Outputs:
# 0001-MdePkg-Add-new-PCD-for-MySoC.patch
# 0002-ArmPkg-Support-MySoC-GIC-topology.patch
# 0003-MySoCPkg-Add-platform-memory-map.patch
# With a cover letter (explains the series)
git format-patch --cover-letter origin/main..HEAD
# Also outputs: 0000-cover-letter.patchPatch File Format
The .patch file produced is RFC 2822 mail format:
From 3a1bc2d4e5f6 Mon Sep 17 00:00:00 2001
From: Your Name <you@company.com>
Date: Mon, 23 Mar 2026 10:00:00 +0000
Subject: [PATCH 1/3] MdePkg: Fix PcdLib null pointer dereference
Details of the change...
Cc: reviewer@tianocore.org
Signed-off-by: Your Name <you@company.com>
---
MdePkg/Library/BasePcdLib/BasePcdLib.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/MdePkg/Library/BasePcdLib/BasePcdLib.c b/MdePkg/Library/BasePcdLib/BasePcdLib.c
index abc123..def456 100644
--- a/MdePkg/Library/BasePcdLib/BasePcdLib.c
+++ b/MdePkg/Library/BasePcdLib/BasePcdLib.c
@@ -45,7 +45,8 @@ LibPcdGet8 (IN UINTN TokenNumber)Applying Patches with git am
# Apply a single patch
git am -3 /path/to/patches/edk2/0001-MdePkg-Fix-PcdLib-null-pointer.patch
# Apply an entire series
git am -3 /path/to/patches/edk2/*.patch
# Apply from stdin (from email)
git am -3 < received-patch.patch
# -3 flag: 3-way merge; if the patch doesn't apply cleanly, attempt a merge
# Without -3: hard fail on context mismatchHandling Patch Conflicts
When git am fails due to a context mismatch (common when rebasing patches onto a newer EDK2 base):
# git am stops with an error like:
# error: patch failed: MdePkg/Library/BasePcdLib/BasePcdLib.c:43
# Patch failed at 0001-MdePkg-Fix-PcdLib-null-pointer.patch
# Option 1: Apply with fuzz factor
git am --abort
git apply --reject --whitespace=fix 0001-MdePkg-Fix-PcdLib-null-pointer.patch
# Manually resolve .rej files, then:
git add MdePkg/Library/BasePcdLib/BasePcdLib.c
git am --continue
# Option 2: Use git apply for inspection first
git apply --check 0001-MdePkg-Fix-PcdLib-null-pointer.patch
# Check which hunks fail
# Option 3: Abort and manually re-create the patch
git am --abort
# Make the change manually, commit, re-format-patchMaintaining a Patch Series with Quilt
quilt is a classical patch management tool that tracks a stack of patches and their application state. It is especially useful for vendor firmware trees that must track many patches over long periods.
Initial Setup
# Install quilt
sudo apt-get install quilt
# Configure quilt to work with git-tracked files
cat > ~/.quiltrc << 'EOF'
QUILT_PATCHES=patches
QUILT_DIFF_ARGS="--no-timestamps --no-index -p ab --color=auto"
QUILT_REFRESH_ARGS="--no-timestamps --no-index -p ab"
QUILT_PATCH_OPTS="--unified"
EOF
cd edk2/
export QUILT_PATCHES=../patches/edk2Quilt Workflow
# See all patches in the series
quilt series
# Apply all patches in the series
quilt push -a
# Apply one patch at a time
quilt push
# Pop (unapply) patches
quilt pop
quilt pop -a # unapply all
# Create a new patch
quilt new 0004-ArmPkg-Fix-timer-interrupt-routing.patch
quilt add ArmPkg/Drivers/TimerDxe/Timer.c # register file with this patch
# Edit the file
vim ArmPkg/Drivers/TimerDxe/Timer.c
# Refresh (update) the patch with current changes
quilt refresh
# Edit the top patch's metadata (commit message analog)
quilt header -eUpdating Patches for a New EDK2 Base
When upgrading the pinned EDK2 commit:
# 1. Save the current patch series
quilt pop -a # unapply all patches from current base
# 2. Update the submodule to the new commit
cd ..
git -C edk2 fetch origin
git -C edk2 checkout <new-commit-sha>
cd edk2
# 3. Re-apply patches one at a time, fixing conflicts
quilt push # apply first patch
# If it fails:
quilt push --fuzz=2 # looser context matching
# Manually fix any rejections, then:
quilt refresh # update the patch with the fixed hunks
quilt push # next patchEDK2 Upstream Contribution Process
If a patch should be merged upstream (not just maintained as a local fix), EDK2 uses an email-based code review process via mailing lists.
Step 1: Identify the Correct Mailing List
Each EDK2 package has a maintainer and mailing list. Check Maintainers.txt at the repo root:
P: MdePkg
M: Michael D Kinney <michael.d.kinney@intel.com>
M: Liming Gao <gaoliming@byosoft.com.cn>
R: Zhiguang Liu <zhiguang.liu@intel.com>
F: MdePkg/
P: ArmPkg
M: Ard Biesheuvel <ardb+tianocore@kernel.org>
M: Leif Lindholm <quic_llindhol@quicinc.com>
F: ArmPkg/Step 2: Check Coding Standards
EDK2 enforces strict code style. Run the checker before submitting:
# ECC (EDK2 Coding Conventions Checker)
python3 BaseTools/Source/Python/Ecc/Ecc.py -c MdePkg -s
# OR use the Ecc plugin via Stuart
stuart_ci_build -c .pytool/CISettings.py --ignore-ci-skip -p MdePkg
# Check copyright header
# Every new file must have:
# SPDX-License-Identifier: BSD-2-Clause-Patent
# and an EDK2 copyright headerStep 3: Send the Patch via Email
# Configure git to use your email
git config --global sendemail.smtpserver smtp.example.com
git config --global sendemail.smtpuser you@example.com
# Send to maintainer and mailing list
git send-email \
--to="devel@edk2.groups.io" \
--cc="michael.d.kinney@intel.com" \
0001-MdePkg-Fix-integer-overflow.patch
# For a series:
git send-email \
--to="devel@edk2.groups.io" \
--cc="ardb+tianocore@kernel.org" \
--annotate \
0000-cover-letter.patch 0001-*.patch 0002-*.patchStep 4: Version a Revised Patch
After review feedback, revise and re-send with a version tag:
# Re-format with version number
git format-patch -v2 origin/main..HEAD
# Outputs: v2-0001-MdePkg-Fix-integer-overflow.patch
# Add changelog in the cover letter and below the --- line in individual patches:---
v2:
- Fix typo in comment (Reviewer Name)
- Add ASSERT for NULL check (Reviewer Name)
- Remove redundant blank line
MdePkg/Library/BasePcdLib/BasePcdLib.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)Out-of-Tree Vendor Patches: Best Practices
Patch Naming Convention
0001-<Package>-<short-description>.patch
│ └── Lowercase, hyphens, imperative mood ("Fix", "Add", "Remove")
└── Prefix number for orderingTagging Patches for Maintainability
Add a tag in the commit message to track why a patch exists:
[VENDOR] MdePkg: Disable feature X for MySoC power saving
[SECURITY-CVE-2024-1234] SecurityPkg: Backport fix for buffer overflow
[BACKPORT-edk2-stable202408] NetworkPkg: TLS 1.3 session ticket
[WORKAROUND] ArmPkg: Disable MMIO cache on rev < 2.0 siliconGenerating a human-readable patch status
# Show which patches are applied and their current status
quilt applied
quilt series --complete
# Or with git: list all commits since the base
git log --oneline <base-commit>..HEADTesting Before Submitting Upstream
# Build test (compile only, no runtime test)
build -p MdePkg/MdePkg.dsc -a X64 -a AARCH64 -t GCC5 -b DEBUG
# Run EDK2 unit tests (if applicable)
build -p UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc -a X64 -t GCC5
# Run the host-side tests
Build/UnitTestFrameworkPkgHostTest/DEBUG_GCC5/X64/SampleUnitTestHost