Skip to content

MsGraphicsPkg: Prevent Recursive Reconnect Crash [Rebase & FF] - #948

Open
Michael Kubacki (makubacki) wants to merge 2 commits into
microsoft:release/202608from
makubacki:prevent_swm_reconnect_r_fault_2608
Open

Michael Kubacki (makubacki) wants to merge 2 commits into
microsoft:release/202608from
makubacki:prevent_swm_reconnect_r_fault_2608

Conversation

@makubacki

@makubacki Michael Kubacki (makubacki) commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Description

reconnect -r currently results in a page fault:

INFO - UninstallProtocolInterface: DD9E7534-7762-4698-8C14-F58517A625AA @ 0x0000000079ba1a40
ERROR - EXCEPTION: PAGE FAULT
ERROR - Accessed Address: 0x100000006
ERROR - Paging Enabled: true
ERROR - Instruction Pointer: 0x79B7CD54
ERROR - Code Segment: 0x38
ERROR - RFLAGS: 0x10206
ERROR - Stack Segment: 0x30
ERROR - Data Segment: 0x30
ERROR - Stack Pointer: 0x79105CF0
ERROR - Page Directory Base: 0x50000
ERROR - Paging Features (cr4): 0x668
ERROR - Error Code: 0x0
ERROR - Page not present
ERROR - R/W: Read
ERROR - Supervisor-mode access violation
ERROR - Data access
ERROR - 
ERROR - Control Registers:
ERROR -  CR0:  0x0000000080010033    CR2:  0x0000000100000006    CR3:  0x0000000000050000    CR4:  0x0000000000000668
ERROR -  RIP:  0x0000000079B7CD54     CS:  0x0000000000000038     SS:  0x0000000000000030     DS:  0x0000000000000030
ERROR -  RSP:  0x0000000079105CF0   RFLAGS:  0x0000000000010206
ERROR - 
ERROR - General-Purpose Registers:
ERROR -  RAX:  0x000000007A22CD58    RBX:  0x0000000079105DD0    RCX:  0x0000000100000002    RDX:  0x0000000000000000
ERROR -  RSI:  0x0000000000000004    RDI:  0x0000000000000000    RBP:  0x0000000079105D30     R8:  0x0000000000000000
ERROR -   R9:  0x0000000000000000    R10:  0x0000000000000000    R11:  0x0000000000000020    R12:  0x0000000000000000
ERROR -  R13:  0x0000000079105E00    R14:  0x0000000079B7CD29    R15:  0x00000000000000B3
INFO - Page Table Range: 0x0000000100000000 - 0x0000000100000FFF
INFO - Flags: G=Global L=Large/Huge page D=Dirty A=Accessed N=Cache disabled T=Write through U=User/K=Kernel W=Writable/R=Read only E=Executable V=Valid
INFO - Root @ 0x50000
INFO -    Pxe  @ 0xFFFFFFFFFFFFF000 Contains 000000007ACFC063  __DA__KWEV  [0x100000000 - 0x7FFFFFFFFF]
INFO -      Ppe  @ 0xFFFFFFFFFFE00020 Contains 0000000000000000  ______KRE_  [0x100000000 - 0x13FFFFFFF]
ERROR - 
ERROR - MTRR Cache Attribute: WriteBack
ERROR - 
ERROR - Dumping Exception Stack Trace:
WARN - Dumping stack trace with PC: 0000000079B7CD54, SP: 0000000079105CF0, FP: 0000000079105D30
WARN -       # Child-SP              Return Address         Call Site
WARN -       0 0000000079105CF0      000000007B0F4B68       SimpleWindowManagerDxe+2D54
WARN -       1 0000000079105D80      000000007A64D6FE       qemu_q35_dxe_core+88B68
WARN -       2 0000000079105E60      000000007B124432       Timer+16FE
WARN -       3 0000000079105EA0      000000007B06E780       qemu_q35_dxe_core+B8432
ERROR - StackTrace: Runtime function not found for module qemu_q35_dxe_core with rip(rva): 2780
ERROR - panicked at C:\Users\mikuback\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\patina_internal_cpu-24.0.0\src\interrupts\x64\interrupt_manager.rs:169:5:
EXCEPTION: PAGE FAULT
WARN - Dumping stack trace with PC: 000000007B1368B6, SP: 000000007B1AB370, FP: 000000007B1AB3B0
WARN -       # Child-SP              Return Address         Call Site
WARN -       0 000000007B1AB370      000000007B085EBA       qemu_q35_dxe_core+CA8B6
WARN -       1 000000007B1AB3C0      000000007B1423AD       qemu_q35_dxe_core+19EBA
ERROR - StackTrace: Runtime function not found for module qemu_q35_dxe_core with rip(rva): D63AD

This DOES NOT update the relevant drivers to follow the UEFI Driver Model as they should. That is a different goal, would require new validation for new usage scenarios which has never happened in the past, and require much more extensive changes. This is just intended to be a minimal fix for the most immediate/serious issues that cause guaranteed invalid pointer dereferences and page faults during the disconnect flow. To address those, some reliance on the driver binding protocol is required.

The immediate issue is that SimpleWindowManagerDxe (SWM) caches the GOP and does not establish a driver-binding relationship with it for disconnect. It does for Absolute Pointer providers, but the timer and cached graphics pointers stay active.

Simple Rendering Engine (SRE) uninstalls the GOP and releases the parent and the SWM's timer event stays active and dereferences stale/invalid pointers.

This PR has two fixes in SWM and the On-Screen Keyboard (OSK) driver that are meant to be minimal fixes intended to reduce risk to the overall driver stack. They primarily target the disconnect/reconnect flow which is currently very broken. In order to address this in SWM, a change had to be made to have it bind to the SRE protocol to get notified during disconnect. While this looks like a non-trivial amount of code change, I believe it is still the minimum/targeted change to address this without continuing to expand scope.

SRE and its relationship to the GOP protocol are not changed and SWM's relationship with Absolute Pointer is not changed. The main relationship established is the binding between SRE and SWM.

With the SWM change made, QEMU and physical platforms can successfully go through a reconnect -r call without crashing. However, the OSK does not restart. The second commit makes minimal changes to address the most impactful problem there.


MsGraphicsPkg/SimpleWindowManagerDxe: Fix graphics lifetime handling during reconnect

SimpleWindowManager aggregates pointer input and provides window-rendering
services through the Simple Rendering Engine (SRE). Previously, its
driver binding only managed pointer devices. Graphics interfaces were
discovered separately and cached without a corresponding disconnect
relationship (did not participate in the driver model).

During a recursive controller reconnect, graphics teardown could
invalidate the cached GOP mode information while SimpleWindowManager's
periodic input callback continued reading it. This caused a page fault
when accessing the screen resolution.

Example page fault on QEMU Q35 (after running reconnect -r):

[OSK]: DriverBindingStop.
INFO - UninstallProtocolInterface: 387477C1-69C7-11D2-8E39-00A0C969723B @ 0x0000000079ba1a28 <-- gEfiSimpleTextInProtocolGuid
INFO - UninstallProtocolInterface: DD9E7534-7762-4698-8C14-F58517A625AA @ 0x0000000079ba1a40 <-- gEfiSimpleTextInputExProtocolGuid
ERROR - EXCEPTION: PAGE FAULT

ERROR - Dumping Exception Stack Trace:
WARN - Dumping stack trace with PC: 0000000079B7CD54, SP: 0000000079105CF0, FP: 0000000079105D30
WARN -       # Child-SP              Return Address         Call Site
WARN -       0 0000000079105CF0      000000007B0F4B68       SimpleWindowManagerDxe+2D54
WARN -       1 0000000079105D80      000000007A64D6FE       qemu_q35_dxe_core+88B68
WARN -       2 0000000079105E60      000000007B124432       Timer+16FE
WARN -       3 0000000079105EA0      000000007B06E780       qemu_q35_dxe_core+B8432

Where SimpleWindowManagerDxe+2D54 maps to
ScreenMaxX = mGop->Mode->Info->HorizontalResolution; in
CheckWatchListCallback() which is a a periodic timer event for
checking the Absolute Pointer protocol watchlist.

This change extends the existing driver binding to manage the private
SRE protocol using BY_DRIVER. On graphics disconnect, the teardown
happens: cancel polling, release rendering surfaces, and clear cached
graphics pointers before releasing the SRE.

On reattachment, graphics interfaces are reacquired, dimensions are
refreshed, client surfaces are recreated, and polling is resumed
without repeating one-time driver initialization.

Client registrations and client-visible paint-event handles are
preserved across graphics restarts. When graphics are unavailable,
rendering operations return EFI_NOT_READY, and failure and unload
cleanup release the associated resources.

The GOP is retrieved from the SRE controller without opening it
BY_DRIVER. This ensures that the graphics console retains ownership
to provide the text console, while existing pointer-device ownership
remains unchanged.


MsGraphicsPkg/OnScreenKeyboardDxe: Fix some restart issues

With the Simple Rendering Engine (SRE) disconnect flow in a
functional state, OSK has its own issues that need to be addressed
to get to a base level of functionality on restart.

  1. Stop() does not close the Device Path protocol that Start()
    opened with BY_DRIVER. Therefore, future calls to Supported()
    will fail.
  2. Stop() and error paths close WaitForKey() and WaitForKeyEx().
    Those are only created in driver entry, so a restarted on-screen
    keyboard will end up publishing closed events to ConSplitter.
  3. Stop() does not call UnregisterClient(), so registering again
    gets back EFI_ALREADY_STARTED (and hits an assert).
  4. Technically, each restart leaks pKeyTextBltBuffer, but that's
    relatively minor and does not directly impact functionality.

These are immediate issues impacting restarting the on-screen keyboard.

This change resolves them by:

  • Recording the GOP handle (that mGop is on).
    OSKCheckDisplayModeTimerCallback() returns early if that handle
    no longer has the mGop protocol instance installed. This is to
    prevent accessing a GOP protocol instance that is no longer valid.
  • Closing the Device Path protocol in Stop().
  • No longer closing WaitForKey() and WaitForKeyEx().
  • Setting timer handles to NULL after closing them to keep state
    clean.
  • Start() now closes the display-mode timer even if the key-repeat
    timer was not created.
  • For the SWM registration (not calling UnregisterClient() in
    Stop()), the keyboard code just keeps its registration. Then,
    on a restart, EFI_ALREADY_STARTED is considered success as long
    as mOSKPaintEvent is set. This was a compromise to make a lower
    risk approach to handling SWM registration on restart, it allows:
    • Other drivers (FrontPage, dialogs, etc.) to continue being able
      to call the OSK protocol during the restart and the SWM keeps
      clients registered across restarts.

This change is meant to be a relatively minimal (reduce risk) approach
to address the most impactful functional issues found in the on-screen
keyboard restart flow. It is not a complete or exhaustive solution to
all potential restart issues. Ideally, the entire stack would need to
be rewritten to more cleanly participate in the UEFI driver model.


Also, Uncrustify changed some code not directly modified.

  • Impacts functionality?
  • Impacts security?
  • Breaking change?
  • Includes tests?
  • Includes documentation?

How This Was Tested

  • Boot QEMU Q35 to EFI shell and run reconnect -r
  • Boot QEMU ArmVirt to EFI shell and run reconnect -r
  • Boot an Intel physical platform to EFI shell and run reconnect -r
  • Verify the OSK without the second commit and does after reconnect -r with the second commit
  • Also, the mouse pointer was not visible in shell after reconnect -r before the changes and is now
  • Boot an Intel physical platform to Windows

Integration Instructions

  • N/A

@makubacki Michael Kubacki (makubacki) changed the title Prevent swm reconnect r fault 2608 MsGraphicsPkg: Prevent Recursive Reconnect Crash [Rebase & FF] Oct 23, 2026
@mu-automation

mu-automation Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

✅ QEMU Validation Passed

Source Dependencies

Repository Commit
mu_plus 246830a
mu_tiano_platforms 9a5b7f8

Results

Platform Target Build Boot Overall Boot Time Build Logs Boot Logs
Q35 DEBUG ✅ success ✅ success 0m 17s Build Logs Boot Logs
ArmVirt DEBUG ✅ success ✅ success 0m 15s Build Logs Boot Logs

Workflow run: https://github.com/microsoft/mu_plus/actions/runs/40357843605

This comment was automatically generated by the Mu QEMU PR Validation workflow.

@makubacki
Michael Kubacki (makubacki) force-pushed the prevent_swm_reconnect_r_fault_2608 branch from 7230550 to 8254fcc Compare October 23, 2026 22:49
Comment thread MsGraphicsPkg/OnScreenKeyboardDxe/OnScreenKeyboardDriver.c Outdated
…during reconnect

SimpleWindowManager aggregates pointer input and provides window-rendering
services through the Simple Rendering Engine (SRE). Previously, its
driver binding only managed pointer devices. Graphics interfaces were
discovered separately and cached without a corresponding disconnect
relationship (did not participate in the driver model).

During a recursive controller reconnect, graphics teardown could
invalidate the cached GOP mode information while SimpleWindowManager's
periodic input callback continued reading it. This caused a page fault
when accessing the screen resolution.

Example page fault on QEMU Q35 (after running `reconnect -r`):

```
[OSK]: DriverBindingStop.
INFO - UninstallProtocolInterface: 387477C1-69C7-11D2-8E39-00A0C969723B @ 0x0000000079ba1a28 <-- gEfiSimpleTextInProtocolGuid
INFO - UninstallProtocolInterface: DD9E7534-7762-4698-8C14-F58517A625AA @ 0x0000000079ba1a40 <-- gEfiSimpleTextInputExProtocolGuid
ERROR - EXCEPTION: PAGE FAULT

ERROR - Dumping Exception Stack Trace:
WARN - Dumping stack trace with PC: 0000000079B7CD54, SP: 0000000079105CF0, FP: 0000000079105D30
WARN -       # Child-SP              Return Address         Call Site
WARN -       0 0000000079105CF0      000000007B0F4B68       SimpleWindowManagerDxe+2D54
WARN -       1 0000000079105D80      000000007A64D6FE       qemu_q35_dxe_core+88B68
WARN -       2 0000000079105E60      000000007B124432       Timer+16FE
WARN -       3 0000000079105EA0      000000007B06E780       qemu_q35_dxe_core+B8432
```

Where `SimpleWindowManagerDxe+2D54` maps to
`ScreenMaxX = mGop->Mode->Info->HorizontalResolution;` in
`CheckWatchListCallback()` which is a a periodic timer event for
checking the Absolute Pointer protocol watchlist.

This change extends the existing driver binding to manage the private
SRE protocol using `BY_DRIVER`. On graphics disconnect, the teardown
happens: cancel polling, release rendering surfaces, and clear cached
graphics pointers before releasing the SRE.

On reattachment, graphics interfaces are reacquired, dimensions are
refreshed, client surfaces are recreated, and polling is resumed
without repeating one-time driver initialization.

Client registrations and client-visible paint-event handles are
preserved across graphics restarts. When graphics are unavailable,
rendering operations return EFI_NOT_READY, and failure and unload
cleanup release the associated resources.

The GOP is retrieved from the SRE controller without opening it
`BY_DRIVER`. This ensures that the graphics console retains ownership
to provide the text console, while existing pointer-device ownership
remains unchanged.

Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com>
With the Simple Rendering Engine (SRE) disconnect flow in a
functional state, OSK has its own issues that need to be addressed
to get to a base level of functionality on restart.

1. `Stop()` does not close the Device Path protocol that `Start()`
   opened with `BY_DRIVER`. Therefore, future calls to `Supported()`
   will fail.
2. `Stop()` and error paths close `WaitForKey()` and `WaitForKeyEx()`.
   Those are only created in driver entry, so a restarted on-screen
   keyboard will end up publishing closed events to ConSplitter.
3. `Stop()` does not call `UnregisterClient()`, so registering again
   gets back `EFI_ALREADY_STARTED` (and hits an assert).
4. Technically, each restart leaks `pKeyTextBltBuffer`, but that's
   relatively minor and does not directly impact functionality.

These are immediate issues impacting restarting the on-screen keyboard.

This change resolves them by:

- Recording the GOP handle (that `mGop` is on).
  `OSKCheckDisplayModeTimerCallback()` returns early if that handle
  no longer has the `mGop` protocol instance installed. This is to
  prevent accessing a GOP protocol instance that is no longer valid.
- Closing the Device Path protocol in `Stop()`.
- No longer closing `WaitForKey()` and `WaitForKeyEx()`.
- Setting timer handles to `NULL` after closing them to keep state
  clean.
- `Start()` now closes the display-mode timer even if the key-repeat
  timer was not created.
- For the SWM registration (not calling `UnregisterClient()` in
  `Stop()`), the keyboard code just keeps its registration. Then,
  on a restart, `EFI_ALREADY_STARTED` is considered success as long
  as `mOSKPaintEvent` is set. This was a compromise to make a lower
  risk approach to handling SWM registration on restart, it allows:
  - Other drivers (FrontPage, dialogs, etc.) to continue being able
    to call the OSK protocol during the restart and the SWM keeps
    clients registered across restarts.

This change is meant to be a relatively minimal (reduce risk) approach
to address the most impactful functional issues found in the on-screen
keyboard restart flow. It is not a complete or exhaustive solution to
all potential restart issues. Ideally, the entire stack would need to
be rewritten to more cleanly participate in the UEFI driver model.

Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com>
@makubacki
Michael Kubacki (makubacki) force-pushed the prevent_swm_reconnect_r_fault_2608 branch from 8254fcc to 246830a Compare October 26, 2026 16:09
@makubacki

Copy link
Copy Markdown
Member Author

This has been a longstanding issue that this PR attempts to resolve. If anyone would like to go about this a different way or contribute their own changes to this PR, please feel free to do so. However, this currently failing with a page fault impacts testing disconnect/reconnect flows on platforms and having it resolved would benefit overall platform testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants