OvmfPkg/VirtioInputDxe: Add virtio mouse support

Virtio mouse devices report relative movements (EV_REL) and button
inputs. Add VirtioMouse.c implementing mouse capability probing,
EFI_SIMPLE_POINTER_PROTOCOL implementation and event handler that
converts VIRTIO_INPUT_EVENT into EFI_SIMPLE_POINTER_STATE.

The shared event dispatcher in VirtioInput.c now routes EV_REL events
and EV_KEY codes above MAX_KEYBOARD_CODE to the mouse handler, while
keycodes in [0, MAX_KEYBOARD_CODE] still go to the keyboard. A device
is now accepted when it provides a keyboard or a mouse, and the
EFI_SIMPLE_POINTER_PROTOCOL is installed on mouse-capable devices.

Signed-off-by: Jiaqing Zhao <Zhao.Jiaqing@amd.com>
This commit is contained in:
Jiaqing Zhao 2026-06-25 16:16:39 +08:00 committed by mergify[bot]
parent fd63e80508
commit efdbf3c355
5 changed files with 361 additions and 8 deletions

View file

@ -415,8 +415,22 @@ VirtioInputGetDeviceData (
// Key press event received
// DEBUG ((DEBUG_INFO, "%a: ---------------------- \nType: %x Code: %x Value: %x\n",
// __func__, Event.Type, Event.Code, Event.Value));
if (IS_BUTTON_CODE (Event.Code)) {
if (Dev->HasMouse) {
VirtioMouseHandleEvent (Dev, &Event);
}
} else if (Dev->HasKeyboard) {
VirtioKeyboardHandleEvent (Dev, &Event);
}
break;
case EV_REL:
// Relative pointer movement received
if (Dev->HasMouse) {
VirtioMouseHandleEvent (Dev, &Event);
}
VirtioKeyboardHandleEvent (Dev, &Event);
break;
default:
@ -534,7 +548,8 @@ VirtioInputInit (
// Check input device capabilities
//
Dev->HasKeyboard = VirtioKeyboardProbe (Dev);
if (!Dev->HasKeyboard) {
Dev->HasMouse = VirtioMouseProbe (Dev);
if (!Dev->HasKeyboard && !Dev->HasMouse) {
Status = EFI_UNSUPPORTED;
goto Failed;
}
@ -549,6 +564,13 @@ VirtioInputInit (
}
}
if (Dev->HasMouse) {
Status = VirtioMouseInit (Dev);
if (EFI_ERROR (Status)) {
goto Failed;
}
}
VirtioInputRingFillRx (Dev, 0);
//
@ -604,6 +626,10 @@ VirtioInputUninit (
VirtioKeyboardUninit (Dev);
}
if (Dev->HasMouse) {
VirtioMouseUninit (Dev);
}
//
// Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
// VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
@ -763,8 +789,32 @@ VirtioInputBindingStart (
}
}
if (Dev->HasMouse) {
Status = gBS->InstallMultipleProtocolInterfaces (
&DeviceHandle,
&gEfiSimplePointerProtocolGuid,
&Dev->SimplePointer,
NULL
);
if (EFI_ERROR (Status)) {
goto UninstallKeyboard;
}
}
return EFI_SUCCESS;
UninstallKeyboard:
if (Dev->HasKeyboard) {
gBS->UninstallMultipleProtocolInterfaces (
DeviceHandle,
&gEfiSimpleTextInProtocolGuid,
&Dev->Txt,
&gEfiSimpleTextInputExProtocolGuid,
&Dev->TxtEx,
NULL
);
}
CloseExitBoot:
gBS->CloseEvent (Dev->ExitBoot);
@ -799,21 +849,34 @@ VirtioInputBindingStop (
{
EFI_STATUS Status;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *Txt;
EFI_SIMPLE_POINTER_PROTOCOL *Pointer;
VIRTIO_INPUT_DEV *Dev;
Status = gBS->OpenProtocol (
DeviceHandle, // candidate device
&gEfiSimpleTextInProtocolGuid, // retrieve the RNG iface
&gEfiSimpleTextInProtocolGuid, // retrieve the keyboard iface
(VOID **)&Txt, // target pointer
This->DriverBindingHandle, // requestor driver ident.
DeviceHandle, // lookup req. for dev.
EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no new ref.
);
if (EFI_ERROR (Status)) {
return Status;
}
if (!EFI_ERROR (Status)) {
Dev = VIRTIO_INPUT_FROM_THIS (Txt);
} else {
Status = gBS->OpenProtocol (
DeviceHandle,
&gEfiSimplePointerProtocolGuid,
(VOID **)&Pointer,
This->DriverBindingHandle,
DeviceHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
Dev = VIRTIO_INPUT_FROM_THIS (Txt);
Dev = VIRTIO_INPUT_FROM_POINTER_THIS (Pointer);
}
//
// Handle Stop() requests for in-use driver instances gracefully.
@ -832,6 +895,18 @@ VirtioInputBindingStop (
}
}
if (Dev->HasMouse) {
Status = gBS->UninstallMultipleProtocolInterfaces (
DeviceHandle,
&gEfiSimplePointerProtocolGuid,
&Dev->SimplePointer,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
}
gBS->CloseEvent (Dev->ExitBoot);
VirtioInputUninit (Dev);

View file

@ -12,11 +12,14 @@
#include <Protocol/ComponentName.h>
#include <Protocol/DriverBinding.h>
#include <Protocol/SimplePointer.h>
#include <Protocol/SimpleTextIn.h>
#include <Protocol/SimpleTextInEx.h>
#include <IndustryStandard/Virtio.h>
#include "VirtioKeyCodes.h"
#define VIRTIO_INPUT_SIG SIGNATURE_32 ('V', 'I', 'N', 'P')
#define MAX_RINGS 2
@ -26,7 +29,10 @@
#define PROBE_TIME_MS 50
// Max range of recognized keyboard codes
#define MAX_KEYBOARD_CODE 255
#define MAX_KEYBOARD_CODE (BTN_MISC - 1)
// Key code 0x100 ~ 0x15f range is for all kinds of button events.
#define IS_BUTTON_CODE(Code) (((Code) >= BTN_MISC) && ((Code) < KEY_OK))
typedef struct {
UINTN Signature;
@ -78,6 +84,13 @@ typedef struct {
LIST_ENTRY KeyNotifyList;
BOOLEAN KeyActive[MAX_KEYBOARD_CODE + 1]; // Key modifiers
BOOLEAN KeyReady;
// Mouse implementation
BOOLEAN HasMouse;
EFI_SIMPLE_POINTER_PROTOCOL SimplePointer;
EFI_SIMPLE_POINTER_MODE PointerMode;
EFI_SIMPLE_POINTER_STATE PointerState;
BOOLEAN PointerReady;
} VIRTIO_INPUT_DEV;
// Helper functions to extract VIRTIO_INPUT_DEV structure pointers
@ -85,6 +98,8 @@ typedef struct {
CR (KbrPointer, VIRTIO_INPUT_DEV, Txt, VIRTIO_INPUT_SIG)
#define VIRTIO_INPUT_EX_FROM_THIS(KbrPointer) \
CR (KbrPointer, VIRTIO_INPUT_DEV, TxtEx, VIRTIO_INPUT_SIG)
#define VIRTIO_INPUT_FROM_POINTER_THIS(a) \
CR (a, VIRTIO_INPUT_DEV, SimplePointer, VIRTIO_INPUT_SIG)
// Bellow candidates to be included as Linux header
#define KEY_PRESSED 1
@ -130,3 +145,27 @@ VOID
VirtioKeyboardUninit (
IN OUT VIRTIO_INPUT_DEV *Dev
);
//
// VirtioMouse.c
//
BOOLEAN
VirtioMouseProbe (
IN VIRTIO_INPUT_DEV *Dev
);
VOID
VirtioMouseHandleEvent (
IN OUT VIRTIO_INPUT_DEV *Dev,
IN VIRTIO_INPUT_EVENT *Event
);
EFI_STATUS
VirtioMouseInit (
IN OUT VIRTIO_INPUT_DEV *Dev
);
VOID
VirtioMouseUninit (
IN OUT VIRTIO_INPUT_DEV *Dev
);

View file

@ -20,6 +20,7 @@
VirtioInput.h
VirtioKeyCodes.h
VirtioKeyboard.c
VirtioMouse.c
[Packages]
MdePkg/MdePkg.dec
@ -40,3 +41,4 @@
gEfiSimpleTextInProtocolGuid
gEfiSimpleTextInputExProtocolGuid
gVirtioDeviceProtocolGuid
gEfiSimplePointerProtocolGuid

View file

@ -18,6 +18,7 @@
*/
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
/*
* Keys and buttons
@ -289,3 +290,16 @@
#define KEY_MICMUTE 248 /* Mute / unmute the microphone */
/* Code 255 is reserved for special needs of AT keyboard driver */
#define BTN_MISC 0x100
#define BTN_LEFT 0x110
#define BTN_RIGHT 0x111
#define KEY_OK 0x160
/*
* Relative axes
*/
#define REL_X 0x00
#define REL_Y 0x01

View file

@ -0,0 +1,223 @@
/** @file
EFI_SIMPLE_POINTER_PROTOCOL implementation for virtio mouse.
Copyright (C) 2026, Advanced Micro Devices, Inc.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/VirtioLib.h>
#include <IndustryStandard/VirtioInput.h>
#include "VirtioInput.h"
#include "VirtioKeyCodes.h"
BOOLEAN
VirtioMouseProbe (
IN VIRTIO_INPUT_DEV *Dev
)
{
EFI_STATUS Status;
UINT8 Size;
UINT8 Bitmap;
// Check if REL_X and REL_Y are set in the EV_REL bitmap
Status = VirtioInputConfigQuerySize (Dev, VirtioInputCfgEvBits, EV_REL, &Size);
if (EFI_ERROR (Status) || (Size == 0)) {
return FALSE;
}
Status = Dev->VirtIo->ReadDevice (Dev->VirtIo, OFFSET_OF_VINPUT (Data), 1, 1, &Bitmap);
if (EFI_ERROR (Status)) {
return FALSE;
}
return (Bitmap & (1 << REL_X)) && (Bitmap & (1 << REL_Y));
}
// -----------------------------------------------------------------------------
// Function handling VirtIO mouse events
VOID
VirtioMouseHandleEvent (
IN OUT VIRTIO_INPUT_DEV *Dev,
IN VIRTIO_INPUT_EVENT *Event
)
{
switch (Event->Type) {
case EV_KEY:
switch (Event->Code) {
case BTN_LEFT:
Dev->PointerState.LeftButton = (BOOLEAN)(Event->Value == KEY_PRESSED);
break;
case BTN_RIGHT:
Dev->PointerState.RightButton = (BOOLEAN)(Event->Value == KEY_PRESSED);
break;
default:
break;
}
Dev->PointerReady = TRUE;
break;
case EV_REL:
switch (Event->Code) {
case REL_X:
Dev->PointerState.RelativeMovementX += (INT32)Event->Value;
break;
case REL_Y:
Dev->PointerState.RelativeMovementY += (INT32)Event->Value;
break;
default:
break;
}
Dev->PointerReady = TRUE;
break;
default:
break;
}
}
// -----------------------------------------------------------------------------
// EFI_SIMPLE_POINTER_PROTOCOL API
STATIC
EFI_STATUS
EFIAPI
VirtioMouseReset (
IN EFI_SIMPLE_POINTER_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
VIRTIO_INPUT_DEV *Dev;
EFI_TPL OldTpl;
Dev = VIRTIO_INPUT_FROM_POINTER_THIS (This);
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
ZeroMem (&Dev->PointerState, sizeof (Dev->PointerState));
Dev->PointerReady = FALSE;
gBS->RestoreTPL (OldTpl);
return EFI_SUCCESS;
}
// -----------------------------------------------------------------------------
// EFI_SIMPLE_POINTER_PROTOCOL API
STATIC
EFI_STATUS
EFIAPI
VirtioMouseGetState (
IN EFI_SIMPLE_POINTER_PROTOCOL *This,
OUT EFI_SIMPLE_POINTER_STATE *State
)
{
VIRTIO_INPUT_DEV *Dev;
EFI_TPL OldTpl;
if (State == NULL) {
return EFI_INVALID_PARAMETER;
}
Dev = VIRTIO_INPUT_FROM_POINTER_THIS (This);
if (!Dev->PointerReady) {
return EFI_NOT_READY;
}
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
CopyMem (State, &Dev->PointerState, sizeof (*State));
//
// Clear mouse state
//
Dev->PointerState.RelativeMovementX = 0;
Dev->PointerState.RelativeMovementY = 0;
Dev->PointerReady = FALSE;
gBS->RestoreTPL (OldTpl);
return EFI_SUCCESS;
}
// -----------------------------------------------------------------------------
// EFI_SIMPLE_POINTER_PROTOCOL WaitForInput event handler
STATIC
VOID
EFIAPI
VirtioMouseWaitForPointer (
IN EFI_EVENT Event,
IN VOID *Context
)
{
VIRTIO_INPUT_DEV *Dev = Context;
//
// Stall 1ms to give other timer-driven drivers a chance to run while this
// routine is recursively invoked from WaitForEvent ().
//
gBS->Stall (1000);
// Drain pending events from the device.
VirtioInputTimer (NULL, Dev);
// If there is new pointer activity - send signal
if (Dev->PointerReady) {
gBS->SignalEvent (Event);
}
}
EFI_STATUS
VirtioMouseInit (
IN OUT VIRTIO_INPUT_DEV *Dev
)
{
EFI_STATUS Status;
Dev->SimplePointer.Reset = VirtioMouseReset;
Dev->SimplePointer.GetState = VirtioMouseGetState;
Dev->SimplePointer.Mode = &Dev->PointerMode;
Dev->PointerMode.ResolutionX = 8;
Dev->PointerMode.ResolutionY = 8;
Dev->PointerMode.LeftButton = TRUE;
Dev->PointerMode.RightButton = TRUE;
ZeroMem (&Dev->PointerState, sizeof (Dev->PointerState));
Dev->PointerReady = FALSE;
//
// Setup the WaitForInput event
//
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_NOTIFY,
VirtioMouseWaitForPointer,
Dev,
&Dev->SimplePointer.WaitForInput
);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
VOID
VirtioMouseUninit (
IN OUT VIRTIO_INPUT_DEV *Dev
)
{
gBS->CloseEvent (Dev->SimplePointer.WaitForInput);
}