Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pcilmr for Windows

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.

Why is the driver folder called driver-v2/?

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).

Testing status: what we did, and what you must still do

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.cmd produces lmrfilter.sys (x64 KMDF) under /W4 /WX, with /Qspectre /guard:cf /GS and /INTEGRITYCHECK; dumpbin confirms Control Flow Guard, Force Integrity, NX, and stack cookies in the PE.
  • INF validates. infverif /v accepts lmrfilter.inf as 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:

  1. Sign the driver so Windows will load it (see Signing — this is the real wall).
  2. Set your target port's hardware ID in lmrfilter.inf (it ships with a VEN_XXXX&DEV_XXXX... placeholder) and generate your own ExtensionId GUID.
  3. 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_CAPABILITIES exercised.
  4. Answer the open question: does BUS_INTERFACE_STANDARD::SetBusData actually reach the Margining Lane Control register on real silicon? tools\lmrprobe.c exists 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.

Signing: why it won't just load

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.

Trying it on a test machine

⚠️ 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 for lmrfilter.inf, it will be oemNN.inf), then pnputil /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.

Building

  • Driver: WDK + cmd /c driver-v2\build-driver.cmd (details above). VS 2026 does not ship the KMDF driver project system, so lmrfilter.vcxproj can't be built by MSBuild there; the script drives the WDK compiler/linker directly. Loading the built .sys still 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.

Design & safety

Why this is safe to read and, eventually, to run — everything is scoped down and opt-in:

  • Passive filter, supported access. WdfFdoInitSetFilter auto-forwards every request; the PCI port stack's behavior is unchanged. Config space is read via BUS_INTERFACE_STANDARD::GetBusData from the device stack — no ECAM mapping.
  • Opt-in that gates the bus traffic itself. Attaching is not enough; an admin sets LmrAuthorized=1 on the device's hardware key. D0Entry checks authorization before any config-space read, so an unauthorized instance walks no capabilities and issues no GetBusData reads 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 reaches READY; capabilities are re-read from fresh config data on every D0 entry.
  • Locked-down control device. GET_CAPABILITIES and margining live on a single control device \\.\LmrLink, secured with SDDL_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 goes MARGINING → 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 via SetBusData is 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.

Risk framing

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.

What each environment can do

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.

Layout

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

References

  • 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-command 0x9C38).
  • 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).

License

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.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages