PCIe Lane Margining at the Receiver (LMR) on Windows: a small, auditable KMDF
filter driver, lmrfilter.sys, that reaches the LMR configuration registers
through the Windows-supported BUS_INTERFACE_STANDARD, plus a user-mode probe
tool. It's a standalone driver — not a drop-in backend for Linux's pcilmr,
which relies on generic config read/write this driver deliberately doesn't expose
(it uses semantic margining IOCTLs instead).
Linux has had this for years (pcilmr, in pciutils). Windows has no in-box
equivalent and no safe, supported way for a user tool to reach the LMR extended
capability registers. This project is an attempt to close that gap.
Status: builds and passes every static gate; never loaded on hardware. The driver compiles clean with the WDK (
/W4 /WX,/Qspectre /guard:cf /GS,/INTEGRITYCHECK), passes InfVerif and Microsoft's CodeQL driver queries, and its user-mode logic is unit-tested. It has not been loaded on a machine or used to margin a real link — that needs a driver signature Windows will accept (in practice Microsoft attestation) and Gen4+ hardware to validate against. See Testing status, Signing, and Trying it on a test machine.
Because the obvious first design was a dead end. v1 mapped ECAM (extended
config space) directly and gated writes with an allow-list. Windows does not
support raw ECAM mapping from a driver — it bugchecks on machines with an ACPI
SDEV table and virtualization-based security — so v1 was scrapped. driver-v2/
is the ground-up redesign: a filter that uses the supported
BUS_INTERFACE_STANDARD, exposes semantic operations instead of raw config
writes, is opt-in per port, and fails closed. The -v2 name is kept as a marker
that the tempting raw-access approach doesn't work; v1 itself isn't shipped here
(it lives in git history).
Done — all zero hardware risk, because nothing was ever loaded:
- User-mode unit tests pass. Capability parsing, the link-lifetime state machine, the count-gated control-device lifecycle (508 event sequences, 9,228 assertions), and the margining command model (every encoding cross-checked against the PCIe Base Specification's bit layout).
- Real WDK compile/link.
driver-v2\build-driver.cmdproduceslmrfilter.sys(x64 KMDF) under/W4 /WX, with/Qspectre /guard:cf /GSand/INTEGRITYCHECK; dumpbin confirms Control Flow Guard, Force Integrity, NX, and stack cookies in the PE. - INF validates.
infverif /vacceptslmrfilter.infas a Universal extension INF. - CodeQL — Microsoft's current driver static-analysis path (SDV is retired) — runs clean.
Not done — you must do this yourself, on hardware you can afford to recover:
- Sign the driver so Windows will load it (see Signing — this is the real wall).
- Set your target port's hardware ID in
lmrfilter.inf(it ships with aVEN_XXXX&DEV_XXXX...placeholder) and generate your ownExtensionIdGUID. - Load on a spare or VM machine — never your main PC — and survive Driver
Verifier plus PnP cycling (start/stop, disable/enable, sleep/resume,
surprise-removal) with only
GET_CAPABILITIESexercised. - Answer the open question: does
BUS_INTERFACE_STANDARD::SetBusDataactually reach the Margining Lane Control register on real silicon?tools\lmrprobe.cexists to test exactly that. Until it is answered on a signed build against a real Gen4+ port, the write path is unproven.
If you only want margin numbers today and don't need Windows, a Linux live USB with
sudo pcilmr (from pciutils) sidesteps all of this.
A KMDF driver must be signed with a certificate the OS trusts, and on current Windows that is a high bar — it is the reason this project stops at "builds and passes every gate":
- Test-signing (
bcdedit /set testsigning on+ a self-made certificate) is the normal developer path, but it requires Secure Boot off, and on a machine with Smart App Control or an enforced WDAC policy it is overridden: the OS refuses a self-test-signed kernel driver regardless, as a clean load failure (CM_PROB 39), not a crash. Turning Smart App Control off is permanent — re-enabling it needs a Windows reinstall — so it is not a step to take on a machine you rely on. - Production loading needs Microsoft attestation signing: an EV code-signing certificate (hardware token, identity-vetted, on the order of a few hundred USD/year) plus a Microsoft Partner Center hardware-developer account. You submit the driver to Microsoft's portal and get back a Microsoft-signed catalog the OS will load. There is no free shortcut — Let's Encrypt and ordinary (non-EV) code-signing certificates do not qualify for kernel attestation.
So finishing hardware validation requires either an EV certificate + attestation, or a dedicated test machine with Secure Boot off and no Smart App Control.
⚠️ A spare or virtual machine only — never a PC or data you care about. This is experimental, never-loaded kernel code. A bug can BSOD the machine, corrupt unsaved work, or cause a boot/start failure that needs Safe Mode / WinRE to fix. Read the risk framing first.
Prerequisites
- A machine you can fully reinstall, with a Gen4+ downstream port.
- Secure Boot off (BIOS/UEFI) — required for test-signing.
- No Smart App Control / enforced WDAC — they override test-signing (see above).
- The WDK installed (
winget install --id Microsoft.WindowsWDK.10.0.26100).
1. Build the driver
cmd /c driver-v2\build-driver.cmd
Produces driver-v2\build\lmrfilter.sys (unsigned).
2. Point the INF at your port, and give it your own IDs
Edit driver-v2\lmrfilter.inf: replace the PCI\VEN_XXXX&DEV_XXXX... placeholder
with your target downstream port's hardware ID (Device Manager → the PCIe port →
Details → Hardware Ids), and put your own GUID in ExtensionId. For a first test,
pick an empty root port — nothing downstream to disrupt.
3. Enable test-signing and sign
bcdedit /set testsigning on
Reboot, then create a test certificate, sign lmrfilter.sys with page hashes
(signtool sign /ph ...), and produce/sign the lmrfilter.cat catalog. (See
Microsoft Learn: Test-signing a driver package.)
4. Install and authorize (opt-in)
pnputil /add-driver driver-v2\lmrfilter.inf /install
Then set a REG_DWORD value LmrAuthorized = 1 on the target device's
hardware key and restart the device. Until you do, the driver issues no PCI
traffic at all — attaching alone does nothing.
5. Probe the write path
Build and run tools\lmrprobe.c as Administrator. It opens \\.\LmrLink, issues a
few Report commands, and prints whether the receiver echoed them — i.e. whether
SetBusData reached the LMR register.
Recovery — if it misbehaves or won't boot
- Remove the driver: find it with
pnputil /enum-drivers(look forlmrfilter.inf, it will beoemNN.inf), thenpnputil /delete-driver oemNN.inf /uninstall. Or Device Manager → the port → Uninstall device / roll back. - If Windows won't boot: enter Safe Mode or WinRE, delete the driver as
above or disable the service (
sc delete lmrfilter), reboot. - When finished, restore the machine:
bcdedit /set testsigning off(and reboot), and delete any test certificate you added.
- Driver: WDK +
cmd /c driver-v2\build-driver.cmd(details above). VS 2026 does not ship the KMDF driver project system, solmrfilter.vcxprojcan't be built by MSBuild there; the script drives the WDK compiler/linker directly. Loading the built.sysstill requires signing (see above). - Unit tests (Windows):
cmd /c tests\run-tests-msvc.cmd(MSVC, no Docker). - Unit tests (any gcc/clang):
sh tests/run-tests.sh— runs with-Werror+ ASan/UBSan in a Linux shell, WSL, or any container; it apt-installs gcc if missing.
Why this is safe to read and, eventually, to run — everything is scoped down and opt-in:
- Passive filter, supported access.
WdfFdoInitSetFilterauto-forwards every request; the PCI port stack's behavior is unchanged. Config space is read viaBUS_INTERFACE_STANDARD::GetBusDatafrom the device stack — no ECAM mapping. - Opt-in that gates the bus traffic itself. Attaching is not enough; an admin
sets
LmrAuthorized=1on the device's hardware key.D0Entrychecks authorization before any config-space read, so an unauthorized instance walks no capabilities and issues noGetBusDatareads at all (one hardware ID can match several ports, so this matters). Only an authorized instance is validated (downstream-port type, LMR capability, max and negotiated width, vendor id not all-ones) and reachesREADY; capabilities are re-read from fresh config data on every D0 entry. - Locked-down control device.
GET_CAPABILITIESand margining live on a single control device\\.\LmrLink, secured withSDDL_DEVOBJ_SYS_ALL_ADM_ALL. The PCI stack's own security is never altered. User mode never supplies a raw offset or register value — it names a semantic operation, and the kernel builds and bounds-checks every write. - Count-gated control device (Microsoft toaster/sideband pattern). Created when
the attached-instance count goes 0→1, deleted when it goes 1→0, all under one wait
lock. Honest availability contract, modeled in
tests/test_ctrl.c(508 event sequences, 9,228 assertions). - Fail-closed lifetime.
UNAVAILABLE → STARTED → VALIDATED → READY → MARGINING, with any PnP/power/removal event failing closed (a live session goesMARGINING → ABORTING → UNAVAILABLE); revocation during a session can never resume. The state machine is exhaustively tested. - Margining behind a transport abstraction.
STEP_TIMING,STEP_VOLTAGE,SET_ERROR_LIMIT,NORMAL: the kernel builds each register value; user mode never supplies one. Fail-closed link-state (speed/width) sampling around every step. Open question, kept behind the transport layer: whether writing the LMR extended capability viaSetBusDatais a supported operation on real silicon. - No Link Control writes, ever. ASPM is disabled through Windows' own power setting; the driver only verifies the resulting hardware state.
The driver has no PCI configuration-write path in its read-only mode (only
BUS_INTERFACE_STANDARD::GetBusData), so its hardware risk is lower than v1's raw
ECAM. But it is still experimental kernel code: a lifetime/locking/PnP/buffer bug
can BSOD the machine it is loaded on, a crash during disk writes can lose unsaved
work or corrupt filesystem state, and attaching an upper filter to a PCIe root port
can cause a boot/start failure that needs Safe Mode / WinRE rather than a plain
reboot. "Read-only" is also not "no bus activity": D0Entry issues short
back-to-back config-space reads while walking the capability lists. Load it only
after the WDK build, static analysis, Driver Verifier and spare-hardware stages,
never on a machine you care about. Driver Verifier + a VM is a strong crash-bug
gate, not proof of hardware safety: a virtual PCI topology cannot exercise the real
BUS_INTERFACE_STANDARD, the LMR capability, root-port power states or the specific
silicon — which is why the spare physical machine stage still matters.
Known KMDF/PnP items still to prove on hardware (unit tests can't cover these): FDO
object lifetime vs. an outstanding IOCTL under surprise-removal,
WdfDeviceQueryProperty location formatting, and control-device teardown vs.
in-flight IOCTLs.
| Environment | Build | Load driver | Real margining |
|---|---|---|---|
| Any gcc / MSVC | ✅ unit tests | ❌ (no Windows kernel) | ❌ |
| Hyper-V / VM | ✅ | ✅ crash/Verifier testing | ❌ virtual PCI has no LMR capability |
| Spare physical PC, Gen4+ | ✅ | ✅ | ✅ |
| Main PC | not until the production bar: attestation-signed, CodeQL, Verifier, HVCI/Secure Boot on |
Note: pcilmr -c ("capabilities only") is not read-only — it sends report
commands (lane-control writes) and prepares/restores the link. Never margin a link
carrying the boot NVMe, the primary GPU, or the network controller, or anything
downstream of them.
| Path | What |
|---|---|
driver-v2\lmrfilter.c |
KMDF filter: BUS_INTERFACE_STANDARD, PnP/power, authorization gate, control-device lifecycle, IOCTL dispatch, margining IO |
driver-v2\lmrcaps.c/.h |
PCIe/LMR capability parsing + downstream-port validation. Transport-agnostic (all reads via a callback) |
driver-v2\lmrmargin.c/.h |
Margining command model + fail-closed step protocol (encodings per the PCIe Base Specification) |
driver-v2\lmrstate.h |
Link lifetime state machine as one pure transition function |
driver-v2\lmrfilter.vcxproj / .inf / build-driver.cmd |
WDK project, template filter INF (placeholder hardware ID), and the direct WDK build script |
include\lmrv2_ioctl.h |
The semantic IOCTL interface, shared by driver and tools |
tools\lmrprobe.c |
Minimal probe: does SetBusData reach the LMR register? |
tests\ |
User-mode unit tests + WDF stub headers for -fsyntax-only driver compile-checks |
- Microsoft Learn: Accessing PCI Device Configuration Space (supported methods, SDEV/VBS bugcheck, header/capability ownership); Test-signing a driver package.
- microsoft/Windows-driver-samples
general/toaster/toastDrv/kmdf/filter/sideband(MIT): the canonical filter + sideband control-device pattern this driver's control-device lifecycle follows. - pciutils
pcilmr/lib/header.h(GPL): the Linux reference implementation and the source of the margining command set this driver builds. - opencomputeproject/ocp-diag-pci_lmt (MIT): a second, readable margining implementation. Independently confirms the Lane Control layout and command encoding (
0x8 + lane*4; receiver[2:0]/type[5:3]/usage[6]/payload[15:8]; no-command0x9C38). - Intel PCIe LMT (doc 639433): proprietary binary tool. Behavioral reference only — not reused; confirms Windows LMR needs a kernel driver.
- chipsec/chipsec (unrestricted access driver, a contrast), namazso/PawnIO (restricted signed modules).
GPL-2.0-or-later — see COPYING. Released under the GPL by choice. This is
original code; its margining command encodings follow the PCIe Base Specification
(facts, not copyrightable) and were informed by Linux's pcilmr
(pciutils), the reference implementation.
You are free to use it, including commercially and inside other GPL projects; if you
distribute a work built on it, you must offer that work's complete corresponding
source under the GPL as well.
No warranty. This is experimental, never-loaded kernel code — see the risk framing above.