diff --git a/packaging/windows/drivers/pf-vdisplay/build.rs b/packaging/windows/drivers/pf-vdisplay/build.rs index bc7bdd4..e2ae2cc 100644 --- a/packaging/windows/drivers/pf-vdisplay/build.rs +++ b/packaging/windows/drivers/pf-vdisplay/build.rs @@ -4,6 +4,12 @@ fn main() -> Result<(), wdk_build::ConfigError> { wdk_build::configure_wdk_binary_build()?; link_iddcx_stub(); + // wdk-build emits `/OPT:REF,ICF`. ICF (Identical COMDAT Folding) merges functions with identical + // bodies into ONE address — our many identical stub IddCx callbacks (`return STATUS_SUCCESS`) collapse + // to the same pointer (and even CRT EH handlers fold, hence the dumpbin `__CxxFrameHandler4 = DllMain` + // alias). The working virtual-display-rs links with NO `/OPT`, and IddCxAdapterInitAsync rejects a + // config whose distinct callbacks alias each other. Disable ICF (REF stays on); last `/OPT` wins. + println!("cargo::rustc-cdylib-link-arg=/OPT:NOICF"); Ok(()) } diff --git a/packaging/windows/drivers/pf-vdisplay/src/adapter.rs b/packaging/windows/drivers/pf-vdisplay/src/adapter.rs index ab8e853..4e4b162 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/adapter.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/adapter.rs @@ -148,6 +148,18 @@ pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS { attr.SynchronizationScope = wdk_sys::_WDF_SYNCHRONIZATION_SCOPE::WdfSynchronizationScopeInheritFromParent; attr.ContextTypeInfo = &ADAPTER_CTX.0; + dbglog!( + "[pf-vd] rt: dev={:#x} pCaps={:#x} model={:#x} mfg={:#x} fwVer={:#x} hwVer={:#x} verSizeOf={} verSet={} diagSet={}", + device as usize, + (&raw const caps) as usize, + caps.EndPointDiagnostics.pEndPointModelName as usize, + caps.EndPointDiagnostics.pEndPointManufacturerName as usize, + caps.EndPointDiagnostics.pFirmwareVersion as usize, + caps.EndPointDiagnostics.pHardwareVersion as usize, + core::mem::size_of::(), + version.Size, + caps.EndPointDiagnostics.Size, + ); let init = iddcx::IDARG_IN_ADAPTER_INIT { WdfDevice: device, pCaps: &raw mut caps, diff --git a/packaging/windows/drivers/pf-vdisplay/src/entry.rs b/packaging/windows/drivers/pf-vdisplay/src/entry.rs index 9a7a3f3..fe3620f 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/entry.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/entry.rs @@ -12,6 +12,26 @@ use wdk_sys::{ use crate::{callbacks, size, STATUS_NOT_FOUND}; +/// A WDF device context, attached to the WDFDEVICE at WdfDeviceCreate. The working virtual-display-rs + +/// oracle both create the device with a context-typed `DeviceContext` (we previously passed +/// WDF_NO_OBJECT_ATTRIBUTES). `WDF_OBJECT_CONTEXT_TYPE_INFO` holds raw pointers (Sync wrapper for the +/// `static`); `UniqueType` self-references per `WDF_DECLARE_CONTEXT_TYPE`. +#[repr(C)] +struct DeviceContext { + _device: WDFDEVICE, +} +#[repr(transparent)] +struct DevCtxInfo(wdk_sys::WDF_OBJECT_CONTEXT_TYPE_INFO); +// SAFETY: immutable 'static type metadata; the inner raw pointers are 'static and never written. +unsafe impl Sync for DevCtxInfo {} +static DEVICE_CTX: DevCtxInfo = DevCtxInfo(wdk_sys::WDF_OBJECT_CONTEXT_TYPE_INFO { + Size: core::mem::size_of::() as u32, + ContextName: c"PfVdDeviceCtx".as_ptr().cast(), + ContextSize: core::mem::size_of::(), + UniqueType: &DEVICE_CTX.0, + EvtDriverGetUniqueContextType: None, +}); + #[unsafe(export_name = "DriverEntry")] pub unsafe extern "system" fn driver_entry( driver: PDRIVER_OBJECT, @@ -78,14 +98,16 @@ extern "C" fn driver_add(_driver: WDFDRIVER, mut init: PWDFDEVICE_INIT) -> NTSTA } let mut device: WDFDEVICE = core::ptr::null_mut(); - // SAFETY: init configured above; no context attributes yet (STEP 3 adds DeviceContext + cleanup). + // Attach a device context type (like the working virtual-display-rs/oracle), not WDF_NO_OBJECT_ATTRIBUTES. + let mut dev_attr: wdk_sys::WDF_OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() }; + dev_attr.Size = core::mem::size_of::() as u32; + dev_attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent; + dev_attr.SynchronizationScope = + wdk_sys::_WDF_SYNCHRONIZATION_SCOPE::WdfSynchronizationScopeInheritFromParent; + dev_attr.ContextTypeInfo = &DEVICE_CTX.0; + // SAFETY: init configured above; dev_attr is a valid context-typed attributes block. let status = unsafe { - call_unsafe_wdf_function_binding!( - WdfDeviceCreate, - &mut init, - WDF_NO_OBJECT_ATTRIBUTES, - &mut device - ) + call_unsafe_wdf_function_binding!(WdfDeviceCreate, &mut init, &mut dev_attr, &mut device) }; dbglog!("[pf-vd] WdfDeviceCreate -> {status:#x}"); if !nt_success(status) { @@ -95,31 +117,14 @@ extern "C" fn driver_add(_driver: WDFDRIVER, mut init: PWDFDEVICE_INIT) -> NTSTA // IddCx must be initialized on the device BEFORE other device setup (the canonical IddCx sample order). // We previously created the device interface first — which can leave IddCx not fully ready by D0Entry, // making IddCxAdapterInitAsync reject (INVALID_PARAMETER) despite byte-perfect caps. + // DIAGNOSTIC (STEP 3): the WdfDeviceCreateDeviceInterface call is REMOVED. Upstream virtual-display-rs + // and the MS IddCx sample create NO device interface — IddCx's control channel is EvtIddCxDeviceIoControl + // (already registered in the config). A non-IddCx device interface registered on the IddCx/IndirectKmd + // stack is a prime suspect for IddCxAdapterInitAsync -> INVALID_PARAMETER. If removing it fixes adapter + // init, the proto control plane moves to EvtIddCxDeviceIoControl (STEP 4) instead of a device interface. + let _ = pf_vdisplay_proto::interface_guid_fields; // keep the dep referenced // SAFETY: device is the just-created WDFDEVICE. let status = unsafe { wdk_iddcx::IddCxDeviceInitialize(device) }; dbglog!("[pf-vd] IddCxDeviceInitialize -> {status:#x}"); - if !nt_success(status) { - return status; - } - - // Expose the owned pf-vdisplay control interface (the host opens this GUID; STEP 4 wires the host - // side in lockstep). NOT SudoVDA's GUID. - let (d1, d2, d3, d4) = pf_vdisplay_proto::interface_guid_fields(); - let guid = GUID { - Data1: d1, - Data2: d2, - Data3: d3, - Data4: d4, - }; - // SAFETY: device is the just-created WDFDEVICE; guid lives for the call; no reference string. - let status = unsafe { - call_unsafe_wdf_function_binding!( - WdfDeviceCreateDeviceInterface, - device, - &guid, - core::ptr::null() - ) - }; - dbglog!("[pf-vd] WdfDeviceCreateDeviceInterface -> {status:#x}"); status }