diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers')
21 files changed, 4139 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportTcpDxe/FastbootTransportTcp.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportTcpDxe/FastbootTransportTcp.c new file mode 100644 index 00000000..c4e20ea5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportTcpDxe/FastbootTransportTcp.c @@ -0,0 +1,660 @@ +/** @file +# +# Copyright (c) 2014, ARM Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +#include <Protocol/AndroidFastbootTransport.h> +#include <Protocol/Dhcp4.h> +#include <Protocol/Tcp4.h> +#include <Protocol/ServiceBinding.h> +#include <Protocol/SimpleTextOut.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/PrintLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiRuntimeServicesTableLib.h> + +#define IP4_ADDR_TO_STRING(IpAddr, IpAddrString) UnicodeSPrint ( \ + IpAddrString, \ + 16 * 2, \ + L"%d.%d.%d.%d", \ + IpAddr.Addr[0], \ + IpAddr.Addr[1], \ + IpAddr.Addr[2], \ + IpAddr.Addr[3] \ + ); + +// Fastboot says max packet size is 512, but FASTBOOT_TRANSPORT_PROTOCOL +// doesn't place a limit on the size of buffers returned by Receive. +// (This isn't actually a packet size - it's just the size of the buffers we +// pass to the TCP driver to fill with received data.) +// We can achieve much better performance by doing this in larger chunks. +#define RX_FRAGMENT_SIZE 2048 + +STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut; + +STATIC EFI_TCP4_PROTOCOL *mTcpConnection; +STATIC EFI_TCP4_PROTOCOL *mTcpListener; + +STATIC EFI_EVENT mReceiveEvent; + +STATIC EFI_SERVICE_BINDING_PROTOCOL *mTcpServiceBinding; +STATIC EFI_HANDLE mTcpHandle = NULL; + +// We only ever use one IO token for receive and one for transmit. To save +// repeatedly allocating and freeing, just allocate statically and re-use. +#define NUM_RX_TOKENS 16 +#define TOKEN_NEXT(Index) (((Index) + 1) % NUM_RX_TOKENS) + +STATIC UINTN mNextSubmitIndex; +STATIC UINTN mNextReceiveIndex; +STATIC EFI_TCP4_IO_TOKEN mReceiveToken[NUM_RX_TOKENS]; +STATIC EFI_TCP4_RECEIVE_DATA mRxData[NUM_RX_TOKENS]; +STATIC EFI_TCP4_IO_TOKEN mTransmitToken; +STATIC EFI_TCP4_TRANSMIT_DATA mTxData; +// We also reuse the accept token +STATIC EFI_TCP4_LISTEN_TOKEN mAcceptToken; +// .. and the close token +STATIC EFI_TCP4_CLOSE_TOKEN mCloseToken; + +// List type for queued received packets +typedef struct _FASTBOOT_TCP_PACKET_LIST { + LIST_ENTRY Link; + VOID *Buffer; + UINTN BufferSize; +} FASTBOOT_TCP_PACKET_LIST; + +STATIC LIST_ENTRY mPacketListHead; + +STATIC +VOID +EFIAPI +DataReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/* + Helper function to set up a receive IO token and call Tcp->Receive +*/ +STATIC +EFI_STATUS +SubmitRecieveToken ( + VOID + ) +{ + EFI_STATUS Status; + VOID *FragmentBuffer; + + Status = EFI_SUCCESS; + + FragmentBuffer = AllocatePool (RX_FRAGMENT_SIZE); + ASSERT (FragmentBuffer != NULL); + if (FragmentBuffer == NULL) { + DEBUG ((EFI_D_ERROR, "TCP Fastboot out of resources")); + return EFI_OUT_OF_RESOURCES; + } + + mRxData[mNextSubmitIndex].DataLength = RX_FRAGMENT_SIZE; + mRxData[mNextSubmitIndex].FragmentTable[0].FragmentLength = RX_FRAGMENT_SIZE; + mRxData[mNextSubmitIndex].FragmentTable[0].FragmentBuffer = FragmentBuffer; + + Status = mTcpConnection->Receive (mTcpConnection, &mReceiveToken[mNextSubmitIndex]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TCP Receive: %r\n", Status)); + FreePool (FragmentBuffer); + } + + mNextSubmitIndex = TOKEN_NEXT (mNextSubmitIndex); + return Status; +} + +/* + Event notify function for when we have closed our TCP connection. + We can now start listening for another connection. +*/ +STATIC +VOID +ConnectionClosed ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its + // PCB from the list of live connections. Subsequent attempts to Configure() + // a TCP instance with the same local port will fail with INVALID_PARAMETER. + // Calling Configure with NULL is a workaround for this issue. + Status = mTcpConnection->Configure (mTcpConnection, NULL); + + mTcpConnection = NULL; + + Status = mTcpListener->Accept (mTcpListener, &mAcceptToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TCP Accept: %r\n", Status)); + } +} + +STATIC +VOID +CloseReceiveEvents ( + VOID + ) +{ + UINTN Index; + + for (Index = 0; Index < NUM_RX_TOKENS; Index++) { + gBS->CloseEvent (mReceiveToken[Index].CompletionToken.Event); + } +} + +/* + Event notify function to be called when we receive TCP data. +*/ +STATIC +VOID +EFIAPI +DataReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + FASTBOOT_TCP_PACKET_LIST *NewEntry; + EFI_TCP4_IO_TOKEN *ReceiveToken; + + ReceiveToken = &mReceiveToken[mNextReceiveIndex]; + + Status = ReceiveToken->CompletionToken.Status; + + if (Status == EFI_CONNECTION_FIN) { + // + // Remote host closed connection. Close our end. + // + + CloseReceiveEvents (); + + Status = mTcpConnection->Close (mTcpConnection, &mCloseToken); + ASSERT_EFI_ERROR (Status); + + return; + } + + // + // Add an element to the receive queue + // + + NewEntry = AllocatePool (sizeof (FASTBOOT_TCP_PACKET_LIST)); + if (NewEntry == NULL) { + DEBUG ((EFI_D_ERROR, "TCP Fastboot: Out of resources\n")); + return; + } + + mNextReceiveIndex = TOKEN_NEXT (mNextReceiveIndex); + + if (!EFI_ERROR (Status)) { + NewEntry->Buffer + = ReceiveToken->Packet.RxData->FragmentTable[0].FragmentBuffer; + NewEntry->BufferSize + = ReceiveToken->Packet.RxData->FragmentTable[0].FragmentLength; + + // Prepare to receive more data + SubmitRecieveToken(); + } else { + // Fatal receive error. Put an entry with NULL in the queue, signifying + // to return EFI_DEVICE_ERROR from TcpFastbootTransportReceive. + NewEntry->Buffer = NULL; + NewEntry->BufferSize = 0; + + DEBUG ((EFI_D_ERROR, "\nTCP Fastboot Receive error: %r\n", Status)); + } + + InsertTailList (&mPacketListHead, &NewEntry->Link); + + Status = gBS->SignalEvent (mReceiveEvent); + ASSERT_EFI_ERROR (Status); +} + + +/* + Event notify function to be called when we accept an incoming TCP connection. +*/ +STATIC +VOID +EFIAPI +ConnectionAccepted ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_TCP4_LISTEN_TOKEN *AcceptToken; + EFI_STATUS Status; + UINTN Index; + + AcceptToken = (EFI_TCP4_LISTEN_TOKEN *) Context; + Status = AcceptToken->CompletionToken.Status; + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TCP Fastboot: Connection Error: %r\n", Status)); + return; + } + DEBUG ((EFI_D_ERROR, "TCP Fastboot: Connection Received.\n")); + + // + // Accepting a new TCP connection creates a new instance of the TCP protocol. + // Open it and prepare to receive on it. + // + + Status = gBS->OpenProtocol ( + AcceptToken->NewChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &mTcpConnection, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Open TCP Connection: %r\n", Status)); + return; + } + + mNextSubmitIndex = 0; + mNextReceiveIndex = 0; + + for (Index = 0; Index < NUM_RX_TOKENS; Index++) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + DataReceived, + NULL, + &(mReceiveToken[Index].CompletionToken.Event) + ); + ASSERT_EFI_ERROR (Status); + } + + for (Index = 0; Index < NUM_RX_TOKENS; Index++) { + SubmitRecieveToken(); + } +} + +/* + Set up TCP Fastboot transport: Configure the network device via DHCP then + start waiting for a TCP connection on the Fastboot port. +*/ +EFI_STATUS +TcpFastbootTransportStart ( + EFI_EVENT ReceiveEvent + ) +{ + EFI_STATUS Status; + EFI_HANDLE NetDeviceHandle; + EFI_HANDLE *HandleBuffer; + EFI_IP4_MODE_DATA Ip4ModeData; + UINTN NumHandles; + CHAR16 IpAddrString[16]; + UINTN Index; + + EFI_TCP4_CONFIG_DATA TcpConfigData = { + 0x00, // IPv4 Type of Service + 255, // IPv4 Time to Live + { // AccessPoint: + TRUE, // Use default address + { {0, 0, 0, 0} }, // IP Address (ignored - use default) + { {0, 0, 0, 0} }, // Subnet mask (ignored - use default) + FixedPcdGet32 (PcdAndroidFastbootTcpPort), // Station port + { {0, 0, 0, 0} }, // Remote address: accept any + 0, // Remote Port: accept any + FALSE // ActiveFlag: be a "server" + }, + NULL // Default advanced TCP options + }; + + mReceiveEvent = ReceiveEvent; + InitializeListHead (&mPacketListHead); + + mTextOut->OutputString (mTextOut, L"Initialising TCP Fastboot transport...\r\n"); + + // + // Open a passive TCP instance + // + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiTcp4ServiceBindingProtocolGuid, + NULL, + &NumHandles, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Find TCP Service Binding: %r\n", Status)); + return Status; + } + + // We just use the first network device + NetDeviceHandle = HandleBuffer[0]; + + Status = gBS->OpenProtocol ( + NetDeviceHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + (VOID **) &mTcpServiceBinding, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Open TCP Service Binding: %r\n", Status)); + return Status; + } + + Status = mTcpServiceBinding->CreateChild (mTcpServiceBinding, &mTcpHandle); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TCP ServiceBinding Create: %r\n", Status)); + return Status; + } + + Status = gBS->OpenProtocol ( + mTcpHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &mTcpListener, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Open TCP Protocol: %r\n", Status)); + } + + // + // Set up re-usable tokens + // + + for (Index = 0; Index < NUM_RX_TOKENS; Index++) { + mRxData[Index].UrgentFlag = FALSE; + mRxData[Index].FragmentCount = 1; + mReceiveToken[Index].Packet.RxData = &mRxData[Index]; + } + + mTxData.Push = TRUE; + mTxData.Urgent = FALSE; + mTxData.FragmentCount = 1; + mTransmitToken.Packet.TxData = &mTxData; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + ConnectionAccepted, + &mAcceptToken, + &mAcceptToken.CompletionToken.Event + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + ConnectionClosed, + &mCloseToken, + &mCloseToken.CompletionToken.Event + ); + ASSERT_EFI_ERROR (Status); + + // + // Configure the TCP instance + // + + Status = mTcpListener->Configure (mTcpListener, &TcpConfigData); + if (Status == EFI_NO_MAPPING) { + // Wait until the IP configuration process (probably DHCP) has finished + do { + Status = mTcpListener->GetModeData (mTcpListener, + NULL, NULL, + &Ip4ModeData, + NULL, NULL + ); + ASSERT_EFI_ERROR (Status); + } while (!Ip4ModeData.IsConfigured); + Status = mTcpListener->Configure (mTcpListener, &TcpConfigData); + } else if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TCP Configure: %r\n", Status)); + return Status; + } + + // + // Tell the user our address and hostname + // + IP4_ADDR_TO_STRING (Ip4ModeData.ConfigData.StationAddress, IpAddrString); + + mTextOut->OutputString (mTextOut, L"TCP Fastboot transport configured."); + mTextOut->OutputString (mTextOut, L"\r\nIP address: "); + mTextOut->OutputString (mTextOut ,IpAddrString); + mTextOut->OutputString (mTextOut, L"\r\n"); + + // + // Start listening for a connection + // + + Status = mTcpListener->Accept (mTcpListener, &mAcceptToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TCP Accept: %r\n", Status)); + return Status; + } + + mTextOut->OutputString (mTextOut, L"TCP Fastboot transport initialised.\r\n"); + + FreePool (HandleBuffer); + + return EFI_SUCCESS; +} + +EFI_STATUS +TcpFastbootTransportStop ( + VOID + ) +{ + EFI_TCP4_CLOSE_TOKEN CloseToken; + EFI_STATUS Status; + UINTN EventIndex; + FASTBOOT_TCP_PACKET_LIST *Entry; + FASTBOOT_TCP_PACKET_LIST *NextEntry; + + // Close any existing TCP connection, blocking until it's done. + if (mTcpConnection != NULL) { + CloseReceiveEvents (); + + CloseToken.AbortOnClose = FALSE; + + Status = gBS->CreateEvent (0, 0, NULL, NULL, &CloseToken.CompletionToken.Event); + ASSERT_EFI_ERROR (Status); + + Status = mTcpConnection->Close (mTcpConnection, &CloseToken); + ASSERT_EFI_ERROR (Status); + + Status = gBS->WaitForEvent ( + 1, + &CloseToken.CompletionToken.Event, + &EventIndex + ); + ASSERT_EFI_ERROR (Status); + + ASSERT_EFI_ERROR (CloseToken.CompletionToken.Status); + + // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its + // PCB from the list of live connections. Subsequent attempts to Configure() + // a TCP instance with the same local port will fail with INVALID_PARAMETER. + // Calling Configure with NULL is a workaround for this issue. + Status = mTcpConnection->Configure (mTcpConnection, NULL); + ASSERT_EFI_ERROR (Status); + } + + + gBS->CloseEvent (mAcceptToken.CompletionToken.Event); + + // Stop listening for connections. + // Ideally we would do this with Cancel, but it isn't implemented by EDK2. + // So we just "reset this TCPv4 instance brutally". + Status = mTcpListener->Configure (mTcpListener, NULL); + ASSERT_EFI_ERROR (Status); + + Status = mTcpServiceBinding->DestroyChild (mTcpServiceBinding, mTcpHandle); + + // Free any data the user didn't pick up + Entry = (FASTBOOT_TCP_PACKET_LIST *) GetFirstNode (&mPacketListHead); + while (!IsNull (&mPacketListHead, &Entry->Link)) { + NextEntry = (FASTBOOT_TCP_PACKET_LIST *) GetNextNode (&mPacketListHead, &Entry->Link); + + RemoveEntryList (&Entry->Link); + if (Entry->Buffer) { + FreePool (Entry->Buffer); + } + FreePool (Entry); + + Entry = NextEntry; + } + + return EFI_SUCCESS; +} + +/* + Event notify function for when data has been sent. Free resources and report + errors. + Context should point to the transmit IO token passed to + TcpConnection->Transmit. +*/ +STATIC +VOID +DataSent ( + EFI_EVENT Event, + VOID *Context + ) +{ + EFI_STATUS Status; + + Status = mTransmitToken.CompletionToken.Status; + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TCP Fastboot transmit result: %r\n", Status)); + gBS->SignalEvent (*(EFI_EVENT *) Context); + } + + FreePool (mTransmitToken.Packet.TxData->FragmentTable[0].FragmentBuffer); +} + +EFI_STATUS +TcpFastbootTransportSend ( + IN UINTN BufferSize, + IN CONST VOID *Buffer, + IN EFI_EVENT *FatalErrorEvent + ) +{ + EFI_STATUS Status; + + if (BufferSize > 512) { + return EFI_INVALID_PARAMETER; + } + + // + // Build transmit IO token + // + + // Create an event so we are notified when a transmission is complete. + // We use this to free resources and report errors. + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + DataSent, + FatalErrorEvent, + &mTransmitToken.CompletionToken.Event + ); + ASSERT_EFI_ERROR (Status); + + mTxData.DataLength = BufferSize; + + mTxData.FragmentTable[0].FragmentLength = BufferSize; + mTxData.FragmentTable[0].FragmentBuffer = AllocateCopyPool ( + BufferSize, + Buffer + ); + + Status = mTcpConnection->Transmit (mTcpConnection, &mTransmitToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TCP Transmit: %r\n", Status)); + return Status; + } + + return EFI_SUCCESS; +} + + +EFI_STATUS +TcpFastbootTransportReceive ( + OUT UINTN *BufferSize, + OUT VOID **Buffer + ) +{ + FASTBOOT_TCP_PACKET_LIST *Entry; + + if (IsListEmpty (&mPacketListHead)) { + return EFI_NOT_READY; + } + + Entry = (FASTBOOT_TCP_PACKET_LIST *) GetFirstNode (&mPacketListHead); + + if (Entry->Buffer == NULL) { + // There was an error receiving this packet. + return EFI_DEVICE_ERROR; + } + + *Buffer = Entry->Buffer; + *BufferSize = Entry->BufferSize; + + RemoveEntryList (&Entry->Link); + FreePool (Entry); + + return EFI_SUCCESS; +} + +FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol = { + TcpFastbootTransportStart, + TcpFastbootTransportStop, + TcpFastbootTransportSend, + TcpFastbootTransportReceive +}; + +EFI_STATUS +TcpFastbootTransportEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + + Status = gBS->LocateProtocol( + &gEfiSimpleTextOutProtocolGuid, + NULL, + (VOID **) &mTextOut + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Fastboot: Open Text Output Protocol: %r\n", Status)); + return Status; + } + + Status = gBS->InstallProtocolInterface ( + &ImageHandle, + &gAndroidFastbootTransportProtocolGuid, + EFI_NATIVE_INTERFACE, + &mTransportProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Fastboot: Install transport Protocol: %r\n", Status)); + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportTcpDxe/FastbootTransportTcpDxe.inf b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportTcpDxe/FastbootTransportTcpDxe.inf new file mode 100644 index 00000000..7f1693a9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportTcpDxe/FastbootTransportTcpDxe.inf @@ -0,0 +1,46 @@ +#/** @file +# +# Copyright (c) 2014, ARM Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TcpFastbootTransportDxe + FILE_GUID = 86787704-8fed-11e3-b3ff-f33b73acfec2 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = TcpFastbootTransportEntryPoint + +[Sources.common] + FastbootTransportTcp.c + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + PrintLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + +[Protocols] + gAndroidFastbootTransportProtocolGuid + gEfiDhcp4ProtocolGuid + gEfiDhcp4ServiceBindingProtocolGuid + gEfiTcp4ServiceBindingProtocolGuid + gEfiSimpleTextOutProtocolGuid + gEfiTcp4ProtocolGuid + gEfiSimpleTextOutProtocolGuid + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + +[FixedPcd] + gEmbeddedTokenSpaceGuid.PcdAndroidFastbootTcpPort diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportUsbDxe/FastbootTransportUsb.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportUsbDxe/FastbootTransportUsb.c new file mode 100644 index 00000000..130b00ad --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportUsbDxe/FastbootTransportUsb.c @@ -0,0 +1,272 @@ +/** @file + + Copyright (c) 2014, ARM Ltd. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +/* + * Implementation of the FASTBOOT_TRANSPORT_PROTOCOL using the USB_DEVICE_PROTOCOL + */ + +#include <Protocol/UsbDevice.h> +#include <Protocol/AndroidFastbootTransport.h> +#include <Protocol/SimpleTextOut.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiDriverEntryPoint.h> + +STATIC USB_DEVICE_PROTOCOL *mUsbDevice; + +// Configuration attributes: +// bit 7 reserved and must be 1, bit 6 means self-powered. +#define CONFIG_DESC_ATTRIBUTES (BIT7 | BIT6) + +#define MAX_PACKET_SIZE_BULK 512 + +STATIC USB_DEVICE_PROTOCOL *mUsbDevice; +STATIC EFI_EVENT mReceiveEvent = NULL; +STATIC LIST_ENTRY mPacketList; + +// List type for queued received packets +typedef struct _FASTBOOT_USB_PACKET_LIST { + LIST_ENTRY Link; + VOID *Buffer; + UINTN BufferSize; +} FASTBOOT_USB_PACKET_LIST; + + +/* + No string descriptors - all string descriptor members are set to 0 +*/ + +STATIC USB_DEVICE_DESCRIPTOR mDeviceDescriptor = { + sizeof (USB_DEVICE_DESCRIPTOR), //Length + USB_DESC_TYPE_DEVICE, //DescriptorType + 0x0200, //BcdUSB + 0xFF, //DeviceClass + 0, //DeviceSubClass + 0, //DeviceProtocol + 64, //MaxPacketSize0 + FixedPcdGet32 (PcdAndroidFastbootUsbVendorId), //IdVendor + FixedPcdGet32 (PcdAndroidFastbootUsbProductId), //IdProduct + 0, //BcdDevice + 0, //StrManufacturer + 0, //StrProduct + 0, //StrSerialNumber + 1 //NumConfigurations +}; + +/* + We have one configuration, one interface, and two endpoints (one IN, one OUT) +*/ + +// Lazy (compile-time) way to concatenate descriptors to pass to the USB device +// protocol + +#pragma pack(1) +typedef struct { + USB_CONFIG_DESCRIPTOR ConfigDescriptor; + USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor1; + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor2; +} GET_CONFIG_DESCRIPTOR_RESPONSE; +#pragma pack() + +STATIC GET_CONFIG_DESCRIPTOR_RESPONSE mGetConfigDescriptorResponse = { + { // USB_CONFIG_DESCRIPTOR + sizeof (USB_CONFIG_DESCRIPTOR), //Length; + USB_DESC_TYPE_CONFIG, //DescriptorType; + sizeof (GET_CONFIG_DESCRIPTOR_RESPONSE), //TotalLength; + 1, //NumInterfaces; + 1, //ConfigurationValue; + 0, //Configuration; + CONFIG_DESC_ATTRIBUTES, //Attributes; + 0 //MaxPower; + }, + { // USB_INTERFACE_DESCRIPTOR + sizeof (USB_INTERFACE_DESCRIPTOR), //Length; + USB_DESC_TYPE_INTERFACE, //DescriptorType; + 0, //InterfaceNumber; + 0, //AlternateSetting; + 2, //NumEndpoints; + 0xFF, //InterfaceClass; + // Vendor specific interface subclass and protocol codes. + // I found these values in the Fastboot code + // (in match_fastboot_with_serial in fastboot.c). + 0x42, //InterfaceSubClass; + 0x03, //InterfaceProtocol; + 0 //Interface; + }, + { // USB_ENDPOINT_DESCRIPTOR (In Endpoint) + sizeof (USB_ENDPOINT_DESCRIPTOR), //Length; + USB_DESC_TYPE_ENDPOINT, //DescriptorType; + 1 | BIT7, //EndpointAddress; + 0x2, //Attributes; + MAX_PACKET_SIZE_BULK, //MaxPacketSize; + 16 //Interval; + }, + { // STATIC USB_ENDPOINT_DESCRIPTOR (Out Endpoint) + sizeof (USB_ENDPOINT_DESCRIPTOR), //Length; + USB_DESC_TYPE_ENDPOINT, //DescriptorType; + 1, //EndpointAddress; + 0x2, //Attributes; + MAX_PACKET_SIZE_BULK, //MaxPacketSize; + 16 //Interval; + } +}; + +STATIC +VOID +DataReceived ( + IN UINTN Size, + IN VOID *Buffer + ) +{ + FASTBOOT_USB_PACKET_LIST *NewEntry; + + NewEntry = AllocatePool (sizeof (*NewEntry)); + ASSERT (NewEntry != NULL); + + NewEntry->Buffer = Buffer; + NewEntry->BufferSize = Size; + + InsertTailList (&mPacketList, &NewEntry->Link); + + if (mReceiveEvent) { + gBS->SignalEvent (mReceiveEvent); + } +} + +STATIC +VOID +DataSent ( + IN UINT8 EndpointIndex + ) +{ + // Don't care. +} + +/* + Set up the transport system for use by Fastboot. + e.g. For USB this probably means making the device enumerable. +*/ +EFI_STATUS +FastbootTransportUsbStart ( + EFI_EVENT ReceiveEvent + ) +{ + GET_CONFIG_DESCRIPTOR_RESPONSE *Responses; + + mReceiveEvent = ReceiveEvent; + + mGetConfigDescriptorResponse.ConfigDescriptor.TotalLength = sizeof (GET_CONFIG_DESCRIPTOR_RESPONSE); + Responses = &mGetConfigDescriptorResponse; + + InitializeListHead (&mPacketList); + + return mUsbDevice->Start (&mDeviceDescriptor, (VOID **) &Responses, DataReceived, DataSent); +} + +/* + Function to be called when all Fastboot transactions are finished, to + de-initialise the transport system. + e.g. A USB OTG system might want to get out of peripheral mode so it can be + a USB host. +*/ +EFI_STATUS +FastbootTransportUsbStop ( + VOID + ) +{ + // not yet implemented in USB + return EFI_SUCCESS; +} + +/* + Send data. This function can be used both for command responses like "OKAY" + and for the data phase (the protocol doesn't describe any situation when the + latter might be necessary, but does allow it) + */ +EFI_STATUS +FastbootTransportUsbSend ( + IN UINTN BufferSize, + IN CONST VOID *Buffer, + IN EFI_EVENT *FatalErrorEvent + ) +{ + // Current USB protocol is blocking, so ignore FatalErrorEvent + return mUsbDevice->Send(1, BufferSize, Buffer); +} + +/* + When the event has been Signalled to say data is available from the host, + this function is used to get data. In order to handle the case where several + packets are received before ReceiveEvent's notify function is called, packets + received are queued, and each call to this function returns the next packet in + the queue. It should therefore be called in a loop, the exit condition being a + return of EFI_NOT_READY. + + Parameters: + Buffer - The buffer in which to place data + BufferSize - The size of Buffer in bytes + + Return EFI_NOT_READY if there is no data available +*/ +EFI_STATUS +FastbootTransportUsbReceive ( + OUT UINTN *BufferSize, + OUT VOID **Buffer + ) +{ + FASTBOOT_USB_PACKET_LIST *Entry; + + if (IsListEmpty (&mPacketList)) { + return EFI_NOT_READY; + } + + Entry = (FASTBOOT_USB_PACKET_LIST *) GetFirstNode (&mPacketList); + + *BufferSize = Entry->BufferSize; + *Buffer = Entry->Buffer; + + RemoveEntryList (&Entry->Link); + FreePool (Entry); + + return EFI_SUCCESS; +} + +STATIC FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol = { + FastbootTransportUsbStart, + FastbootTransportUsbStop, + FastbootTransportUsbSend, + FastbootTransportUsbReceive +}; + +EFI_STATUS +FastbootTransportUsbEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // Assume there's only one USB peripheral controller. + Status = gBS->LocateProtocol (&gUsbDeviceProtocolGuid, NULL, (VOID **) &mUsbDevice); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->InstallProtocolInterface ( + &ImageHandle, + &gAndroidFastbootTransportProtocolGuid, + EFI_NATIVE_INTERFACE, + &mTransportProtocol + ); + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportUsbDxe/FastbootTransportUsbDxe.inf b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportUsbDxe/FastbootTransportUsbDxe.inf new file mode 100644 index 00000000..94c804ff --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/AndroidFastbootTransportUsbDxe/FastbootTransportUsbDxe.inf @@ -0,0 +1,41 @@ +#/** @file +# +# Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FastbootTransportUsbDxe + FILE_GUID = f6bec3fe-88fb-11e3-ae84-e73b77561c35 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = FastbootTransportUsbEntryPoint + +[Sources.common] + FastbootTransportUsb.c + +[LibraryClasses] + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEfiDriverBindingProtocolGuid + gUsbDeviceProtocolGuid + gAndroidFastbootTransportProtocolGuid + gEfiSimpleTextOutProtocolGuid + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + +[FixedPcd] + gEmbeddedTokenSpaceGuid.PcdAndroidFastbootUsbVendorId + gEmbeddedTokenSpaceGuid.PcdAndroidFastbootUsbProductId diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c new file mode 100644 index 00000000..e92d0fd7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.c @@ -0,0 +1,288 @@ +/** @file +* +* Copyright (c) 2017, Linaro, Ltd. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include <Uefi.h> +#include <IndustryStandard/Acpi.h> +#include <libfdt.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/DevicePathLib.h> +#include <Library/HiiLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> + +#include <Protocol/AcpiTable.h> +#include <Protocol/AcpiSystemDescriptionTable.h> + +#include "ConsolePrefDxe.h" + +#define SPCR_SIG EFI_ACPI_2_0_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE + +extern UINT8 ConsolePrefHiiBin[]; +extern UINT8 ConsolePrefDxeStrings[]; + +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; + +STATIC HII_VENDOR_DEVICE_PATH mConsolePrefDxeVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + CONSOLE_PREF_FORMSET_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +STATIC EFI_EVENT mReadyToBootEvent; + +STATIC +EFI_STATUS +InstallHiiPages ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HII_HANDLE HiiHandle; + EFI_HANDLE DriverHandle; + + DriverHandle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces (&DriverHandle, + &gEfiDevicePathProtocolGuid, + &mConsolePrefDxeVendorDevicePath, + NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + HiiHandle = HiiAddPackages (&gConsolePrefFormSetGuid, + DriverHandle, + ConsolePrefDxeStrings, + ConsolePrefHiiBin, + NULL); + + if (HiiHandle == NULL) { + gBS->UninstallMultipleProtocolInterfaces (DriverHandle, + &gEfiDevicePathProtocolGuid, + &mConsolePrefDxeVendorDevicePath, + NULL); + return EFI_OUT_OF_RESOURCES; + } + return EFI_SUCCESS; +} + +STATIC +VOID +RemoveDtStdoutPath ( + VOID +) +{ + VOID *Dtb; + INT32 Node; + INT32 Error; + EFI_STATUS Status; + + Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &Dtb); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "%a: could not retrieve DT blob - %r\n", __FUNCTION__, + Status)); + return; + } + + Node = fdt_path_offset (Dtb, "/chosen"); + if (Node < 0) { + return; + } + + Error = fdt_delprop (Dtb, Node, "stdout-path"); + if (Error) { + DEBUG ((DEBUG_INFO, "%a: Failed to delete 'stdout-path' property: %a\n", + __FUNCTION__, fdt_strerror (Error))); + } +} + +STATIC +VOID +RemoveSpcrTable ( + VOID + ) +{ + EFI_ACPI_SDT_PROTOCOL *Sdt; + EFI_ACPI_TABLE_PROTOCOL *AcpiTable; + EFI_STATUS Status; + UINTN TableIndex; + EFI_ACPI_SDT_HEADER *TableHeader; + EFI_ACPI_TABLE_VERSION TableVersion; + UINTN TableKey; + + Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, + (VOID **)&AcpiTable); + if (EFI_ERROR (Status)) { + return; + } + + Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (VOID **)&Sdt); + if (EFI_ERROR (Status)) { + return; + } + + TableIndex = 0; + TableKey = 0; + TableHeader = NULL; + + do { + Status = Sdt->GetAcpiTable (TableIndex++, &TableHeader, &TableVersion, + &TableKey); + if (EFI_ERROR (Status)) { + break; + } + + if (TableHeader->Signature != SPCR_SIG) { + continue; + } + + Status = AcpiTable->UninstallAcpiTable (AcpiTable, TableKey); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "%a: failed to uninstall SPCR table - %r\n", + __FUNCTION__, Status)); + } + break; + } while (TRUE); +} + +STATIC +VOID +EFIAPI +OnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + CONSOLE_PREF_VARSTORE_DATA ConsolePref; + UINTN BufferSize; + EFI_STATUS Status; + VOID *Gop; + + BufferSize = sizeof (ConsolePref); + Status = gRT->GetVariable (CONSOLE_PREF_VARIABLE_NAME, + &gConsolePrefFormSetGuid, NULL, &BufferSize, &ConsolePref); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, + "%a: variable '%s' could not be read - bailing!\n", __FUNCTION__, + CONSOLE_PREF_VARIABLE_NAME)); + return; + } + + if (ConsolePref.Console == CONSOLE_PREF_SERIAL) { + DEBUG ((DEBUG_INFO, + "%a: serial console preferred - doing nothing\n", __FUNCTION__)); + return; + } + + // + // Check if any GOP instances exist: if so, disable stdout-path and SPCR + // + Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, &Gop); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, + "%a: no GOP instances found - doing nothing (%r)\n", __FUNCTION__, + Status)); + return; + } + + RemoveDtStdoutPath (); + RemoveSpcrTable (); +} + +/** + The entry point for ConsolePrefDxe driver. + + @param[in] ImageHandle The image handle of the driver. + @param[in] SystemTable The system table. + + @retval EFI_ALREADY_STARTED The driver already exists in system. + @retval EFI_OUT_OF_RESOURCES Fail to execute entry point due to lack of + resources. + @retval EFI_SUCCESS All the related protocols are installed on + the driver. + +**/ +EFI_STATUS +EFIAPI +ConsolePrefDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + CONSOLE_PREF_VARSTORE_DATA ConsolePref; + UINTN BufferSize; + + // + // Get the current console preference from the ConsolePref variable. + // + BufferSize = sizeof (ConsolePref); + Status = gRT->GetVariable (CONSOLE_PREF_VARIABLE_NAME, + &gConsolePrefFormSetGuid, NULL, &BufferSize, &ConsolePref); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, + "%a: no console preference found, defaulting to graphical\n", + __FUNCTION__)); + ConsolePref.Console = CONSOLE_PREF_GRAPHICAL; + } + + if (!EFI_ERROR (Status) && + ConsolePref.Console != CONSOLE_PREF_GRAPHICAL && + ConsolePref.Console != CONSOLE_PREF_SERIAL) { + DEBUG ((DEBUG_WARN, "%a: invalid value for %s, defaulting to graphical\n", + __FUNCTION__, CONSOLE_PREF_VARIABLE_NAME)); + ConsolePref.Console = CONSOLE_PREF_GRAPHICAL; + Status = EFI_INVALID_PARAMETER; // trigger setvar below + } + + // + // Write the newly selected value back to the variable store. + // + if (EFI_ERROR (Status)) { + ZeroMem (&ConsolePref.Reserved, sizeof (ConsolePref.Reserved)); + Status = gRT->SetVariable (CONSOLE_PREF_VARIABLE_NAME, + &gConsolePrefFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (ConsolePref), &ConsolePref); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: gRT->SetVariable () failed - %r\n", + __FUNCTION__, Status)); + return Status; + } + } + + Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, + OnReadyToBoot, NULL, &gEfiEventReadyToBootGuid, + &mReadyToBootEvent); + ASSERT_EFI_ERROR (Status); + + return InstallHiiPages (); +} diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.h b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.h new file mode 100644 index 00000000..8d87ca90 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.h @@ -0,0 +1,25 @@ +/** @file +* +* Copyright (c) 2017, Linaro Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef __CONSOLE_PREF_DXE_H__ +#define __CONSOLE_PREF_DXE_H__ + +#include <Guid/HiiPlatformSetupFormset.h> +#include <Guid/ConsolePrefFormSet.h> + +#define CONSOLE_PREF_GRAPHICAL 0x0 +#define CONSOLE_PREF_SERIAL 0x1 + +#define CONSOLE_PREF_VARIABLE_NAME L"ConsolePref" + +typedef struct { + UINT8 Console; + UINT8 Reserved[3]; +} CONSOLE_PREF_VARSTORE_DATA; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.inf b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.inf new file mode 100644 index 00000000..aa58142a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefDxe.inf @@ -0,0 +1,55 @@ +## @file +# +# Copyright (c) 2017, Linaro, Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = ConsolePrefDxe + FILE_GUID = bbe2668c-0efc-46fb-9137-4f2da8f419f3 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = ConsolePrefDxeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 +# + +[Sources] + ConsolePrefDxe.c + ConsolePrefHii.vfr + ConsolePrefHii.uni + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + FdtLib + HiiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + UefiRuntimeServicesTableLib + +[Guids] + gConsolePrefFormSetGuid + gFdtTableGuid + gEfiEventReadyToBootGuid + +[Protocols] + gEfiAcpiTableProtocolGuid + gEfiAcpiSdtProtocolGuid + gEfiGraphicsOutputProtocolGuid + +[Depex] + gEfiVariableArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefHii.uni b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefHii.uni new file mode 100644 index 00000000..d1c532b1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefHii.uni @@ -0,0 +1,21 @@ +/** @file
+*
+* Copyright (c) 2017, Linaro, Ltd. All rights reserved.
+*
+* SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#langdef en-US "English"
+
+#string STR_FORM_SET_TITLE #language en-US "Console Preference Selection"
+#string STR_FORM_SET_TITLE_HELP #language en-US "Press <Enter> to choose between graphical and serial console."
+
+#string STR_MAIN_FORM_TITLE #language en-US "Console Preference Selection"
+#string STR_NULL_STRING #language en-US ""
+
+#string STR_CONSOLE_PREF_SELECT_PROMPT #language en-US "Preferred console"
+#string STR_CONSOLE_PREF_SELECT_HELP #language en-US "Select the preferred console if both graphical and serial are available."
+
+#string STR_CONSOLE_PREF_GRAPHICAL #language en-US "Graphical"
+#string STR_CONSOLE_PREF_SERIAL #language en-US "Serial"
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefHii.vfr b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefHii.vfr new file mode 100644 index 00000000..719f99e3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/ConsolePrefDxe/ConsolePrefHii.vfr @@ -0,0 +1,38 @@ +/** @file +* +* Copyright (c) 2017, Linaro, Ltd. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include <Uefi/UefiMultiPhase.h> +#include "ConsolePrefDxe.h" + +formset + guid = CONSOLE_PREF_FORMSET_GUID, + title = STRING_TOKEN(STR_FORM_SET_TITLE), + help = STRING_TOKEN(STR_FORM_SET_TITLE_HELP), + classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID, + + efivarstore CONSOLE_PREF_VARSTORE_DATA, + attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, // EFI variable attributes + name = ConsolePref, + guid = CONSOLE_PREF_FORMSET_GUID; + + form formid = 0x1000, + title = STRING_TOKEN(STR_MAIN_FORM_TITLE); + + oneof varid = ConsolePref.Console, + prompt = STRING_TOKEN(STR_CONSOLE_PREF_SELECT_PROMPT), + help = STRING_TOKEN(STR_CONSOLE_PREF_SELECT_HELP), + flags = NUMERIC_SIZE_1 | INTERACTIVE, + option text = STRING_TOKEN(STR_CONSOLE_PREF_GRAPHICAL), value = CONSOLE_PREF_GRAPHICAL, flags = DEFAULT; + option text = STRING_TOKEN(STR_CONSOLE_PREF_SERIAL), value = CONSOLE_PREF_SERIAL, flags = 0; + endoneof; + + subtitle text = STRING_TOKEN(STR_NULL_STRING); + + endform; + +endformset; diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.c new file mode 100644 index 00000000..ef092a60 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.c @@ -0,0 +1,208 @@ +/** @file +* +* Copyright (c) 2017, Linaro, Ltd. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/DevicePathLib.h> +#include <Library/DtPlatformDtbLoaderLib.h> +#include <Library/HiiLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiRuntimeServicesTableLib.h> + +#include "DtPlatformDxe.h" + +extern UINT8 DtPlatformHiiBin[]; +extern UINT8 DtPlatformDxeStrings[]; + +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; + +STATIC HII_VENDOR_DEVICE_PATH mDtPlatformDxeVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + DT_PLATFORM_FORMSET_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + +STATIC +EFI_STATUS +InstallHiiPages ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HII_HANDLE HiiHandle; + EFI_HANDLE DriverHandle; + + DriverHandle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces (&DriverHandle, + &gEfiDevicePathProtocolGuid, + &mDtPlatformDxeVendorDevicePath, + NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + HiiHandle = HiiAddPackages (&gDtPlatformFormSetGuid, + DriverHandle, + DtPlatformDxeStrings, + DtPlatformHiiBin, + NULL); + + if (HiiHandle == NULL) { + gBS->UninstallMultipleProtocolInterfaces (DriverHandle, + &gEfiDevicePathProtocolGuid, + &mDtPlatformDxeVendorDevicePath, + NULL); + return EFI_OUT_OF_RESOURCES; + } + return EFI_SUCCESS; +} + +/** + The entry point for DtPlatformDxe driver. + + @param[in] ImageHandle The image handle of the driver. + @param[in] SystemTable The system table. + + @retval EFI_ALREADY_STARTED The driver already exists in system. + @retval EFI_OUT_OF_RESOURCES Fail to execute entry point due to lack of + resources. + @retval EFI_SUCCESS All the related protocols are installed on + the driver. + +**/ +EFI_STATUS +EFIAPI +DtPlatformDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + DT_ACPI_VARSTORE_DATA DtAcpiPref; + UINTN BufferSize; + VOID *Dtb; + UINTN DtbSize; + + Dtb = NULL; + Status = DtPlatformLoadDtb (&Dtb, &DtbSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, + "%a: no DTB blob could be loaded, defaulting to ACPI (Status == %r)\n", + __FUNCTION__, Status)); + DtAcpiPref.Pref = DT_ACPI_SELECT_ACPI; + } else { + // + // Get the current DT/ACPI preference from the DtAcpiPref variable. + // + BufferSize = sizeof (DtAcpiPref); + Status = gRT->GetVariable(DT_ACPI_VARIABLE_NAME, &gDtPlatformFormSetGuid, + NULL, &BufferSize, &DtAcpiPref); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "%a: no DT/ACPI preference found, defaulting to %a\n", + __FUNCTION__, PcdGetBool (PcdDefaultDtPref) ? "DT" : "ACPI")); + DtAcpiPref.Pref = PcdGetBool (PcdDefaultDtPref) ? DT_ACPI_SELECT_DT + : DT_ACPI_SELECT_ACPI; + } + } + + if (!EFI_ERROR (Status) && + DtAcpiPref.Pref != DT_ACPI_SELECT_ACPI && + DtAcpiPref.Pref != DT_ACPI_SELECT_DT) { + DEBUG ((DEBUG_WARN, "%a: invalid value for %s, defaulting to %a\n", + __FUNCTION__, DT_ACPI_VARIABLE_NAME, + PcdGetBool (PcdDefaultDtPref) ? "DT" : "ACPI")); + DtAcpiPref.Pref = PcdGetBool (PcdDefaultDtPref) ? DT_ACPI_SELECT_DT + : DT_ACPI_SELECT_ACPI; + Status = EFI_INVALID_PARAMETER; // trigger setvar below + } + + // + // Write the newly selected default value back to the variable store. + // + if (EFI_ERROR (Status)) { + Status = gRT->SetVariable(DT_ACPI_VARIABLE_NAME, &gDtPlatformFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (DtAcpiPref), &DtAcpiPref); + if (EFI_ERROR (Status)) { + goto FreeDtb; + } + } + + if (DtAcpiPref.Pref == DT_ACPI_SELECT_ACPI) { + // + // ACPI was selected: install the gEdkiiPlatformHasAcpiGuid GUID as a + // NULL protocol to unlock dispatch of ACPI related drivers. + // + Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle, + &gEdkiiPlatformHasAcpiGuid, NULL, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, + "%a: failed to install gEdkiiPlatformHasAcpiGuid as a protocol\n", + __FUNCTION__)); + goto FreeDtb; + } + } else if (DtAcpiPref.Pref == DT_ACPI_SELECT_DT) { + // + // DT was selected: copy the blob into newly allocated memory and install + // a reference to it as the FDT configuration table. + // + Status = gBS->InstallConfigurationTable (&gFdtTableGuid, Dtb); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to install FDT configuration table\n", + __FUNCTION__)); + goto FreeDtb; + } + } else { + ASSERT (FALSE); + } + + // + // No point in installing the HII pages if ACPI is the only description + // we have + // + if (Dtb == NULL) { + return EFI_SUCCESS; + } + + // + // Note that we don't uninstall the gEdkiiPlatformHasAcpiGuid protocol nor + // the FDT configuration table if the following call fails. While that will + // cause loading of this driver to fail, proceeding with ACPI and DT both + // disabled will guarantee a failed boot, and so it is better to leave them + // installed in that case. + // + return InstallHiiPages (); + +FreeDtb: + if (Dtb != NULL) { + FreePool (Dtb); + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.h b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.h new file mode 100644 index 00000000..6a9d3962 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.h @@ -0,0 +1,25 @@ +/** @file +* +* Copyright (c) 2017, Linaro Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef __DT_PLATFORM_DXE_H__ +#define __DT_PLATFORM_DXE_H__ + +#include <Guid/HiiPlatformSetupFormset.h> +#include <Guid/DtPlatformFormSet.h> + +#define DT_ACPI_SELECT_DT 0x0 +#define DT_ACPI_SELECT_ACPI 0x1 + +#define DT_ACPI_VARIABLE_NAME L"DtAcpiPref" + +typedef struct { + UINT8 Pref; + UINT8 Reserved[3]; +} DT_ACPI_VARSTORE_DATA; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.inf b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.inf new file mode 100644 index 00000000..7beccd34 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformDxe.inf @@ -0,0 +1,54 @@ +## @file +# +# Copyright (c) 2017, Linaro, Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = DtPlatformDxe + FILE_GUID = FC097B3C-2EBD-4A75-A3DA-121DCAB365CC + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DtPlatformDxeEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 +# + +[Sources] + DtPlatformDxe.c + DtPlatformHii.vfr + DtPlatformHii.uni + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + DtPlatformDtbLoaderLib + HiiLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + +[Guids] + gDtPlatformFormSetGuid + gDtPlatformDefaultDtbFileGuid + gEdkiiPlatformHasAcpiGuid + gFdtTableGuid + +[Pcd] + gEmbeddedTokenSpaceGuid.PcdDefaultDtPref + +[Depex] + gEfiVariableArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformHii.uni b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformHii.uni new file mode 100644 index 00000000..5f24061a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformHii.uni @@ -0,0 +1,21 @@ +/** @file
+*
+* Copyright (c) 2017, Linaro, Ltd. All rights reserved.
+*
+* SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#langdef en-US "English"
+
+#string STR_FORM_SET_TITLE #language en-US "O/S Hardware Description Selection"
+#string STR_FORM_SET_TITLE_HELP #language en-US "Press <Enter> to choose between ACPI and DT hardware descriptions."
+
+#string STR_MAIN_FORM_TITLE #language en-US "O/S Hardware Description Selection"
+#string STR_NULL_STRING #language en-US ""
+
+#string STR_DT_ACPI_SELECT_PROMPT #language en-US "O/S Hardware Description"
+#string STR_DT_ACPI_SELECT_HELP #language en-US "Select the hardware description that will be exposed to the O/S."
+
+#string STR_DT_ACPI_SELECT_DT #language en-US "Device Tree"
+#string STR_DT_ACPI_SELECT_ACPI #language en-US "ACPI"
diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformHii.vfr b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformHii.vfr new file mode 100644 index 00000000..99d03bad --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/DtPlatformDxe/DtPlatformHii.vfr @@ -0,0 +1,38 @@ +/** @file +* +* Copyright (c) 2017, Linaro, Ltd. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include <Uefi/UefiMultiPhase.h> +#include "DtPlatformDxe.h" + +formset + guid = DT_PLATFORM_FORMSET_GUID, + title = STRING_TOKEN(STR_FORM_SET_TITLE), + help = STRING_TOKEN(STR_FORM_SET_TITLE_HELP), + classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID, + + efivarstore DT_ACPI_VARSTORE_DATA, + attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, // EFI variable attributes + name = DtAcpiPref, + guid = DT_PLATFORM_FORMSET_GUID; + + form formid = 0x1000, + title = STRING_TOKEN(STR_MAIN_FORM_TITLE); + + oneof varid = DtAcpiPref.Pref, + prompt = STRING_TOKEN(STR_DT_ACPI_SELECT_PROMPT), + help = STRING_TOKEN(STR_DT_ACPI_SELECT_HELP), + flags = NUMERIC_SIZE_1 | INTERACTIVE | RESET_REQUIRED, + option text = STRING_TOKEN(STR_DT_ACPI_SELECT_DT), value = DT_ACPI_SELECT_DT, flags = DEFAULT; + option text = STRING_TOKEN(STR_DT_ACPI_SELECT_ACPI), value = DT_ACPI_SELECT_ACPI, flags = 0; + endoneof; + + subtitle text = STRING_TOKEN(STR_NULL_STRING); + + endform; + +endformset; diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/NonCoherentIoMmuDxe/NonCoherentIoMmuDxe.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/NonCoherentIoMmuDxe/NonCoherentIoMmuDxe.c new file mode 100644 index 00000000..48605463 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/NonCoherentIoMmuDxe/NonCoherentIoMmuDxe.c @@ -0,0 +1,250 @@ +/** @file + + Copyright (c) 2019, Linaro, Ltd. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/DmaLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Protocol/IoMmu.h> + +/** + Set IOMMU attribute for a system memory. + + If the IOMMU protocol exists, the system memory cannot be used + for DMA by default. + + When a device requests a DMA access for a system memory, + the device driver need use SetAttribute() to update the IOMMU + attribute to request DMA access (read and/or write). + + The DeviceHandle is used to identify which device submits the request. + The IOMMU implementation need translate the device path to an IOMMU device + ID, and set IOMMU hardware register accordingly. + 1) DeviceHandle can be a standard PCI device. + The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. + The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. + The memory for BusMasterCommonBuffer need set + EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. + After the memory is used, the memory need set 0 to keep it being + protected. + 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). + The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or + EDKII_IOMMU_ACCESS_WRITE. + + @param[in] This The protocol instance pointer. + @param[in] DeviceHandle The device who initiates the DMA access + request. + @param[in] Mapping The mapping value returned from Map(). + @param[in] IoMmuAccess The IOMMU access. + + @retval EFI_SUCCESS The IoMmuAccess is set for the memory range + specified by DeviceAddress and Length. + @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + Map(). + @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination + of access. + @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. + @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported + by the IOMMU. + @retval EFI_UNSUPPORTED The IOMMU does not support the memory range + specified by Mapping. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + modify the IOMMU access. + @retval EFI_DEVICE_ERROR The IOMMU device reported an error while + attempting the operation. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoMmuSetAttribute ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN VOID *Mapping, + IN UINT64 IoMmuAccess + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Provides the controller-specific addresses required to access system memory + from a DMA bus master. On SEV guest, the DMA operations must be performed on + shared buffer hence we allocate a bounce buffer to map the HostAddress to a + DeviceAddress. The Encryption attribute is removed from the DeviceAddress + buffer. + + @param This The protocol instance pointer. + @param Operation Indicates if the bus master is going to read or + write to system memory. + @param HostAddress The system memory address to map to the PCI + controller. + @param NumberOfBytes On input the number of bytes to map. On output + the number of bytes that were mapped. + @param DeviceAddress The resulting map address for the bus master + PCI controller to use to access the hosts + HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned + NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common + buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested + address. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoMmuMap ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + DMA_MAP_OPERATION DmaOperation; + + switch (Operation) { + case EdkiiIoMmuOperationBusMasterRead: + case EdkiiIoMmuOperationBusMasterRead64: + DmaOperation = MapOperationBusMasterRead; + break; + + case EdkiiIoMmuOperationBusMasterWrite: + case EdkiiIoMmuOperationBusMasterWrite64: + DmaOperation = MapOperationBusMasterWrite; + break; + + case EdkiiIoMmuOperationBusMasterCommonBuffer: + case EdkiiIoMmuOperationBusMasterCommonBuffer64: + DmaOperation = MapOperationBusMasterCommonBuffer; + break; + + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + return DmaMap (DmaOperation, HostAddress, NumberOfBytes, + DeviceAddress, Mapping); +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This The protocol instance pointer. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system + memory. +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoMmuUnmap ( + IN EDKII_IOMMU_PROTOCOL *This, + IN VOID *Mapping + ) +{ + return DmaUnmap (Mapping); +} + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param This The protocol instance pointer. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, + EfiBootServicesData or EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory + address of the allocated range. + @param Attributes The requested bit mask of attributes for the + allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal + attribute bits are MEMORY_WRITE_COMBINE and + MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoMmuAllocateBuffer ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT VOID **HostAddress, + IN UINT64 Attributes + ) +{ + return DmaAllocateBuffer (MemoryType, Pages, HostAddress); +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This The protocol instance pointer. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated + range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and + Pages was not allocated with AllocateBuffer(). + +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoMmuFreeBuffer ( + IN EDKII_IOMMU_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ) +{ + return DmaFreeBuffer (Pages, HostAddress); +} + +STATIC EDKII_IOMMU_PROTOCOL mNonCoherentIoMmuOps = { + EDKII_IOMMU_PROTOCOL_REVISION, + NonCoherentIoMmuSetAttribute, + NonCoherentIoMmuMap, + NonCoherentIoMmuUnmap, + NonCoherentIoMmuAllocateBuffer, + NonCoherentIoMmuFreeBuffer, +}; + + +EFI_STATUS +EFIAPI +NonCoherentIoMmuDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return gBS->InstallMultipleProtocolInterfaces (&ImageHandle, + &gEdkiiIoMmuProtocolGuid, &mNonCoherentIoMmuOps, + NULL); +} diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/NonCoherentIoMmuDxe/NonCoherentIoMmuDxe.inf b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/NonCoherentIoMmuDxe/NonCoherentIoMmuDxe.inf new file mode 100644 index 00000000..12ab8a80 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/NonCoherentIoMmuDxe/NonCoherentIoMmuDxe.inf @@ -0,0 +1,35 @@ +#/** @file +# +# Copyright (c) 2019, Linaro, Ltd. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 1.27 + BASE_NAME = NonCoherentIoMmuDxe + FILE_GUID = 7ed510aa-9cdc-49d2-a306-6e11e359f9b3 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = NonCoherentIoMmuDxeEntryPoint + +[Sources] + NonCoherentIoMmuDxe.c + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + DmaLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEdkiiIoMmuProtocolGuid ## PRODUCES + +[Depex] + TRUE diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/ComponentName.c new file mode 100644 index 00000000..d12f1c63 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/ComponentName.c @@ -0,0 +1,181 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "VirtualKeyboard.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gVirtualKeyboardComponentName = { + VirtualKeyboardComponentNameGetDriverName, + VirtualKeyboardComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gVirtualKeyboardComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) VirtualKeyboardComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) VirtualKeyboardComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mVirtualKeyboardDriverNameTable[] = { + { + "eng;en", + L"Virtual Keyboard Driver" + }, + { + "zh-CHS", + L"虚拟键盘驱动程序" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mVirtualKeyboardDriverNameTable, + DriverName, + (BOOLEAN)(This == &gVirtualKeyboardComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/ComponentName.h b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/ComponentName.h new file mode 100644 index 00000000..45c20332 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/ComponentName.h @@ -0,0 +1,147 @@ +/** @file + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _VIRTUAL_KEYBOARD_COMPONENT_NAME_H_ +#define _VIRTUAL_KEYBOARD_COMPONENT_NAME_H_ + + +extern EFI_COMPONENT_NAME_PROTOCOL gVirtualKeyboardComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gVirtualKeyboardComponentName2; + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboard.c b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboard.c new file mode 100644 index 00000000..c0193aed --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboard.c @@ -0,0 +1,1143 @@ +/** @file + VirtualKeyboard driver + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "VirtualKeyboard.h" + +// +// RAM Keyboard Driver Binding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gVirtualKeyboardDriverBinding = { + VirtualKeyboardDriverBindingSupported, + VirtualKeyboardDriverBindingStart, + VirtualKeyboardDriverBindingStop, + 0x10, + NULL, + NULL +}; + +// +// EFI Driver Binding Protocol Functions +// + +/** + Check whether the driver supports this device. + + @param This The Udriver binding protocol. + @param Controller The controller handle to check. + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The driver supports this controller. + @retval other This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual; + + Status = gBS->OpenProtocol ( + Controller, + &gPlatformVirtualKeyboardProtocolGuid, + (VOID **) &PlatformVirtual, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + gBS->CloseProtocol ( + Controller, + &gPlatformVirtualKeyboardProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; +} + +/** + Starts the device with this driver. + + @param This The driver binding instance. + @param Controller Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The controller is controlled by the driver. + @retval Other This controller cannot be started. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual; + + Status = gBS->OpenProtocol ( + Controller, + &gPlatformVirtualKeyboardProtocolGuid, + (VOID **) &PlatformVirtual, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Allocate the private device structure + // + VirtualKeyboardPrivate = (VIRTUAL_KEYBOARD_DEV *) AllocateZeroPool (sizeof (VIRTUAL_KEYBOARD_DEV)); + if (VirtualKeyboardPrivate == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Initialize the private device structure + // + VirtualKeyboardPrivate->Signature = VIRTUAL_KEYBOARD_DEV_SIGNATURE; + VirtualKeyboardPrivate->Handle = Controller; + VirtualKeyboardPrivate->PlatformVirtual = PlatformVirtual; + VirtualKeyboardPrivate->Queue.Front = 0; + VirtualKeyboardPrivate->Queue.Rear = 0; + VirtualKeyboardPrivate->QueueForNotify.Front = 0; + VirtualKeyboardPrivate->QueueForNotify.Rear = 0; + + VirtualKeyboardPrivate->SimpleTextIn.Reset = VirtualKeyboardReset; + VirtualKeyboardPrivate->SimpleTextIn.ReadKeyStroke = VirtualKeyboardReadKeyStroke; + + VirtualKeyboardPrivate->SimpleTextInputEx.Reset = VirtualKeyboardResetEx; + VirtualKeyboardPrivate->SimpleTextInputEx.ReadKeyStrokeEx = VirtualKeyboardReadKeyStrokeEx; + VirtualKeyboardPrivate->SimpleTextInputEx.SetState = VirtualKeyboardSetState; + + VirtualKeyboardPrivate->SimpleTextInputEx.RegisterKeyNotify = VirtualKeyboardRegisterKeyNotify; + VirtualKeyboardPrivate->SimpleTextInputEx.UnregisterKeyNotify = VirtualKeyboardUnregisterKeyNotify; + InitializeListHead (&VirtualKeyboardPrivate->NotifyList); + + Status = PlatformVirtual->Register (); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Report that the keyboard is being enabled + // + REPORT_STATUS_CODE ( + EFI_PROGRESS_CODE, + EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE + ); + + // + // Setup the WaitForKey event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + VirtualKeyboardWaitForKey, + &(VirtualKeyboardPrivate->SimpleTextIn), + &((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey) + ); + if (EFI_ERROR (Status)) { + (VirtualKeyboardPrivate->SimpleTextIn).WaitForKey = NULL; + goto Done; + } + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_NOTIFY, + VirtualKeyboardWaitForKeyEx, + &(VirtualKeyboardPrivate->SimpleTextInputEx), + &(VirtualKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx) + ); + if (EFI_ERROR (Status)) { + VirtualKeyboardPrivate->SimpleTextInputEx.WaitForKeyEx = NULL; + goto Done; + } + + // + // Setup a periodic timer, used for reading keystrokes at a fixed interval + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + VirtualKeyboardTimerHandler, + VirtualKeyboardPrivate, + &VirtualKeyboardPrivate->TimerEvent + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = gBS->SetTimer ( + VirtualKeyboardPrivate->TimerEvent, + TimerPeriodic, + KEYBOARD_TIMER_INTERVAL + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + KeyNotifyProcessHandler, + VirtualKeyboardPrivate, + &VirtualKeyboardPrivate->KeyNotifyProcessEvent + ); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + // + // Reset the keyboard device + // + Status = VirtualKeyboardPrivate->SimpleTextInputEx.Reset ( + &VirtualKeyboardPrivate->SimpleTextInputEx, + FALSE + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "[KBD]Reset Failed. Status - %r\n", Status)); + goto Done; + } + // + // Install protocol interfaces for the keyboard device. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiSimpleTextInProtocolGuid, + &VirtualKeyboardPrivate->SimpleTextIn, + &gEfiSimpleTextInputExProtocolGuid, + &VirtualKeyboardPrivate->SimpleTextInputEx, + NULL + ); + +Done: + if (EFI_ERROR (Status)) { + if (VirtualKeyboardPrivate != NULL) { + if ((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey != NULL) { + gBS->CloseEvent ((VirtualKeyboardPrivate->SimpleTextIn).WaitForKey); + } + + if ((VirtualKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx != NULL) { + gBS->CloseEvent ( + (VirtualKeyboardPrivate->SimpleTextInputEx).WaitForKeyEx + ); + } + + if (VirtualKeyboardPrivate->KeyNotifyProcessEvent != NULL) { + gBS->CloseEvent (VirtualKeyboardPrivate->KeyNotifyProcessEvent); + } + + VirtualKeyboardFreeNotifyList (&VirtualKeyboardPrivate->NotifyList); + + if (VirtualKeyboardPrivate->TimerEvent != NULL) { + gBS->CloseEvent (VirtualKeyboardPrivate->TimerEvent); + } + FreePool (VirtualKeyboardPrivate); + } + } + + gBS->CloseProtocol ( + Controller, + &gPlatformVirtualKeyboardProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + +/** + Stop the device handled by this driver. + + @param This The driver binding protocol. + @param Controller The controller to release. + @param NumberOfChildren The number of handles in ChildHandleBuffer. + @param ChildHandleBuffer The array of child handle. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a + device error. + @retval Others Fail to uninstall protocols attached on the + device. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + return EFI_SUCCESS; +} + + +/** + Enqueue the key. + + @param Queue The queue to be enqueued. + @param KeyData The key data to be enqueued. + + @retval EFI_NOT_READY The queue is full. + @retval EFI_SUCCESS Successfully enqueued the key data. + +**/ +EFI_STATUS +Enqueue ( + IN SIMPLE_QUEUE *Queue, + IN EFI_KEY_DATA *KeyData + ) +{ + if ((Queue->Rear + 1) % QUEUE_MAX_COUNT == Queue->Front) { + return EFI_NOT_READY; + } + + CopyMem (&Queue->Buffer[Queue->Rear], KeyData, sizeof (EFI_KEY_DATA)); + Queue->Rear = (Queue->Rear + 1) % QUEUE_MAX_COUNT; + + return EFI_SUCCESS; +} + +/** + Dequeue the key. + + @param Queue The queue to be dequeued. + @param KeyData The key data to be dequeued. + + @retval EFI_NOT_READY The queue is empty. + @retval EFI_SUCCESS Successfully dequeued the key data. + +**/ +EFI_STATUS +Dequeue ( + IN SIMPLE_QUEUE *Queue, + IN EFI_KEY_DATA *KeyData + ) +{ + if (Queue->Front == Queue->Rear) { + return EFI_NOT_READY; + } + + CopyMem (KeyData, &Queue->Buffer[Queue->Front], sizeof (EFI_KEY_DATA)); + Queue->Front = (Queue->Front + 1) % QUEUE_MAX_COUNT; + + return EFI_SUCCESS; +} + +/** + Check whether the queue is empty. + + @param Queue The queue to be checked. + + @retval EFI_NOT_READY The queue is empty. + @retval EFI_SUCCESS The queue is not empty. + +**/ +EFI_STATUS +CheckQueue ( + IN SIMPLE_QUEUE *Queue + ) +{ + if (Queue->Front == Queue->Rear) { + return EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +/** + Check key buffer to get the key stroke status. + + @param This Pointer of the protocol EFI_SIMPLE_TEXT_IN_PROTOCOL. + + @retval EFI_SUCCESS A key is being pressed now. + @retval Other No key is now pressed. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardCheckForKey ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This + ) +{ + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + + VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); + + return CheckQueue (&VirtualKeyboardPrivate->Queue); +} + +/** + Free keyboard notify list. + + @param ListHead The list head + + @retval EFI_SUCCESS Free the notify list successfully + @retval EFI_INVALID_PARAMETER ListHead is invalid. + +**/ +EFI_STATUS +VirtualKeyboardFreeNotifyList ( + IN OUT LIST_ENTRY *ListHead + ) +{ + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode; + + if (ListHead == NULL) { + return EFI_INVALID_PARAMETER; + } + while (!IsListEmpty (ListHead)) { + NotifyNode = CR ( + ListHead->ForwardLink, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + RemoveEntryList (ListHead->ForwardLink); + gBS->FreePool (NotifyNode); + } + + return EFI_SUCCESS; +} + +/** + Judge whether is a registed key + + @param RegsiteredData A pointer to a buffer that is filled in with + the keystroke state data for the key that was + registered. + @param InputData A pointer to a buffer that is filled in with + the keystroke state data for the key that was + pressed. + + @retval TRUE Key be pressed matches a registered key. + @retval FALSE Match failed. + +**/ +BOOLEAN +IsKeyRegistered ( + IN EFI_KEY_DATA *RegsiteredData, + IN EFI_KEY_DATA *InputData + ) + +{ + ASSERT (RegsiteredData != NULL && InputData != NULL); + + if ((RegsiteredData->Key.ScanCode != InputData->Key.ScanCode) || + (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) { + return FALSE; + } + + // + // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means + // these state could be ignored. + // + if ((RegsiteredData->KeyState.KeyShiftState != 0) && + (RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState)) { + return FALSE; + } + if ((RegsiteredData->KeyState.KeyToggleState != 0) && + (RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState)) { + return FALSE; + } + + return TRUE; + +} + +/** + Event notification function for SIMPLE_TEXT_IN.WaitForKey event + Signal the event if there is key available + + @param Event the event object + @param Context waiting context + +**/ +VOID +EFIAPI +VirtualKeyboardWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Stall 1ms to give a chance to let other driver interrupt this routine + // for their timer event. + // e.g. UI setup or Shell, other drivers which are driven by timer event + // will have a bad performance during this period, + // e.g. usb keyboard driver. + // Add a stall period can greatly increate other driver performance during + // the WaitForKey is recursivly invoked. 1ms delay will make little impact + // to the thunk keyboard driver, and user can not feel the delay at all when + // input. + // + gBS->Stall (1000); + // + // Use TimerEvent callback function to check whether there's any key pressed + // + VirtualKeyboardTimerHandler (NULL, VIRTUAL_KEYBOARD_DEV_FROM_THIS (Context)); + + if (!EFI_ERROR (VirtualKeyboardCheckForKey (Context))) { + gBS->SignalEvent (Event); + } +} + +/** + Event notification function for SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx + event. Signal the event if there is key available + + @param Event event object + @param Context waiting context + +**/ +VOID +EFIAPI +VirtualKeyboardWaitForKeyEx ( + IN EFI_EVENT Event, + IN VOID *Context + ) + +{ + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + + VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (Context); + VirtualKeyboardWaitForKey (Event, &VirtualKeyboardPrivate->SimpleTextIn); + +} + +// +// EFI Simple Text In Protocol Functions +// +/** + Reset the Keyboard and do BAT test for it, if (ExtendedVerification == TRUE) + then do some extra keyboard validations. + + @param This Pointer of simple text Protocol. + @param ExtendedVerification Whether perform the extra validation of + keyboard. True: perform; FALSE: skip. + + @retval EFI_SUCCESS The command byte is written successfully. + @retval EFI_DEVICE_ERROR Errors occurred during resetting keyboard. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + EFI_STATUS Status; + EFI_TPL OldTpl; + + VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); + + // + // Raise TPL to avoid mouse operation impact + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + if (VirtualKeyboardPrivate->PlatformVirtual && + VirtualKeyboardPrivate->PlatformVirtual->Reset) { + Status = VirtualKeyboardPrivate->PlatformVirtual->Reset (); + } else { + Status = EFI_INVALID_PARAMETER; + } + + // + // resume priority of task level + // + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Reset the input device and optionally run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and + could not be reset. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + EFI_STATUS Status; + EFI_TPL OldTpl; + + VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); + + Status = VirtualKeyboardPrivate->SimpleTextIn.Reset ( + &VirtualKeyboardPrivate->SimpleTextIn, + ExtendedVerification + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +} + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existence of a keystroke via WaitForEvent () call. + + @param VirtualKeyboardPrivate Virtualkeyboard driver private structure. + @param KeyData A pointer to a buffer that is filled in + with the keystroke state data for the key + that was pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data available. + @retval EFI_DEVICE_ERROR The keystroke information was not returned + due to hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +KeyboardReadKeyStrokeWorker ( + IN VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate, + OUT EFI_KEY_DATA *KeyData + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Use TimerEvent callback function to check whether there's any key pressed + // + + // + // Stall 1ms to give a chance to let other driver interrupt this routine for + // their timer event. + // e.g. OS loader, other drivers which are driven by timer event will have a + // bad performance during this period, + // e.g. usb keyboard driver. + // Add a stall period can greatly increate other driver performance during + // the WaitForKey is recursivly invoked. 1ms delay will make little impact + // to the thunk keyboard driver, and user can not feel the delay at all when + // input. + // + gBS->Stall (1000); + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + VirtualKeyboardTimerHandler (NULL, VirtualKeyboardPrivate); + // + // If there's no key, just return + // + Status = CheckQueue (&VirtualKeyboardPrivate->Queue); + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (OldTpl); + return EFI_NOT_READY; + } + + Status = Dequeue (&VirtualKeyboardPrivate->Queue, KeyData); + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +/** + Read out the scan code of the key that has just been stroked. + + @param This Pointer of simple text Protocol. + @param Key Pointer for store the key that read out. + + @retval EFI_SUCCESS The key is read out successfully. + @retval other The key reading failed. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ) +{ + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + EFI_STATUS Status; + EFI_KEY_DATA KeyData; + + VirtualKeyboardPrivate = VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); + + Status = KeyboardReadKeyStrokeWorker (VirtualKeyboardPrivate, &KeyData); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Convert the Ctrl+[a-z] to Ctrl+[1-26] + // + if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) { + if (KeyData.Key.UnicodeChar >= L'a' && + KeyData.Key.UnicodeChar <= L'z') { + KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'a' + 1); + } else if (KeyData.Key.UnicodeChar >= L'A' && + KeyData.Key.UnicodeChar <= L'Z') { + KeyData.Key.UnicodeChar = (CHAR16) (KeyData.Key.UnicodeChar - L'A' + 1); + } + } + + CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY)); + + return EFI_SUCCESS; +} + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existence of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the + keystroke state data for the key that was pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data available. + @retval EFI_DEVICE_ERROR The keystroke information was not returned + due to hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ) +{ + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + + if (KeyData == NULL) { + return EFI_INVALID_PARAMETER; + } + + VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); + + return KeyboardReadKeyStrokeWorker (VirtualKeyboardPrivate, KeyData); + +} + +/** + Set certain state for the input device. + + @param This Protocol instance pointer. + @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the + state for the input device. + + @retval EFI_SUCCESS The device state was set successfully. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and + could not have the setting adjusted. + @retval EFI_UNSUPPORTED The device does not have the ability to set + its state. + @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ) +{ + if (KeyToggleState == NULL) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Register a notification function for a particular keystroke for the + input device. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with + the keystroke information data for the key + that was pressed. + @param KeyNotificationFunction Points to the function to be called when the + key sequence is typed specified by KeyData. + @param NotifyHandle Points to the unique handle assigned to the + registered notification. + + + @retval EFI_SUCCESS The notification function was registered + successfully. + @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necessary + data structures. + @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT VOID **NotifyHandle + ) +{ + EFI_STATUS Status; + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + EFI_TPL OldTpl; + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify; + LIST_ENTRY *Link; + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + + if (KeyData == NULL || + NotifyHandle == NULL || + KeyNotificationFunction == NULL) { + return EFI_INVALID_PARAMETER; + } + + VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already + // registered. + // + for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink; + Link != &VirtualKeyboardPrivate->NotifyList; + Link = Link->ForwardLink) { + CurrentNotify = CR ( + Link, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) { + if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) { + *NotifyHandle = CurrentNotify; + Status = EFI_SUCCESS; + goto Exit; + } + } + } + + // + // Allocate resource to save the notification function + // + + NewNotify = (VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY)); + if (NewNotify == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + NewNotify->Signature = VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE; + NewNotify->KeyNotificationFn = KeyNotificationFunction; + CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA)); + InsertTailList (&VirtualKeyboardPrivate->NotifyList, &NewNotify->NotifyEntry); + + *NotifyHandle = NewNotify; + Status = EFI_SUCCESS; + +Exit: + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + return Status; + +} + +/** + Remove a registered notification function from a particular keystroke. + + @param This Protocol instance pointer. + @param NotificationHandle The handle of the notification function + being unregistered. + + @retval EFI_SUCCESS The notification function was unregistered + successfully. + @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN VOID *NotificationHandle + ) +{ + EFI_STATUS Status; + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + EFI_TPL OldTpl; + LIST_ENTRY *Link; + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + + // + // Check incoming notification handle + // + if (NotificationHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (((VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *) NotificationHandle)->Signature != + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + VirtualKeyboardPrivate = TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS (This); + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink; + Link != &VirtualKeyboardPrivate->NotifyList; + Link = Link->ForwardLink) { + CurrentNotify = CR ( + Link, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (CurrentNotify == NotificationHandle) { + // + // Remove the notification function from NotifyList and free resources + // + RemoveEntryList (&CurrentNotify->NotifyEntry); + + Status = EFI_SUCCESS; + goto Exit; + } + } + + // + // Can not find the specified Notification Handle + // + Status = EFI_INVALID_PARAMETER; + +Exit: + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Timer event handler: read a series of scancodes from 8042 + and put them into memory scancode buffer. + it read as much scancodes to either fill + the memory buffer or empty the keyboard buffer. + It is registered as running under TPL_NOTIFY + + @param Event The timer event + @param Context A KEYBOARD_CONSOLE_IN_DEV pointer + +**/ +VOID +EFIAPI +VirtualKeyboardTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_TPL OldTpl; + LIST_ENTRY *Link; + EFI_KEY_DATA KeyData; + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + VIRTUAL_KBD_KEY VirtualKey; + + VirtualKeyboardPrivate = Context; + + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + if (VirtualKeyboardPrivate->PlatformVirtual && + VirtualKeyboardPrivate->PlatformVirtual->Query) { + if (VirtualKeyboardPrivate->PlatformVirtual->Query (&VirtualKey) == + FALSE) { + goto Exit; + } + // Found key + KeyData.Key.ScanCode = VirtualKey.Key.ScanCode; + KeyData.Key.UnicodeChar = VirtualKey.Key.UnicodeChar; + KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID; + KeyData.KeyState.KeyToggleState = EFI_TOGGLE_STATE_VALID; + if (VirtualKeyboardPrivate->PlatformVirtual->Clear) { + VirtualKeyboardPrivate->PlatformVirtual->Clear (&VirtualKey); + } + } else { + goto Exit; + } + + // + // Signal KeyNotify process event if this key pressed matches any key registered. + // + for (Link = VirtualKeyboardPrivate->NotifyList.ForwardLink; + Link != &VirtualKeyboardPrivate->NotifyList; + Link = Link->ForwardLink) { + CurrentNotify = CR ( + Link, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { + // + // The key notification function needs to run at TPL_CALLBACK + // while current TPL is TPL_NOTIFY. It will be invoked in + // KeyNotifyProcessHandler() which runs at TPL_CALLBACK. + // + Enqueue (&VirtualKeyboardPrivate->QueueForNotify, &KeyData); + gBS->SignalEvent (VirtualKeyboardPrivate->KeyNotifyProcessEvent); + break; + } + } + + Enqueue (&VirtualKeyboardPrivate->Queue, &KeyData); + +Exit: + // + // Leave critical section and return + // + gBS->RestoreTPL (OldTpl); +} + +/** + Process key notify. + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. +**/ +VOID +EFIAPI +KeyNotifyProcessHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VIRTUAL_KEYBOARD_DEV *VirtualKeyboardPrivate; + EFI_KEY_DATA KeyData; + LIST_ENTRY *Link; + LIST_ENTRY *NotifyList; + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify; + EFI_TPL OldTpl; + + VirtualKeyboardPrivate = (VIRTUAL_KEYBOARD_DEV *) Context; + + // + // Invoke notification functions. + // + NotifyList = &VirtualKeyboardPrivate->NotifyList; + while (TRUE) { + // + // Enter critical section + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + Status = Dequeue (&VirtualKeyboardPrivate->QueueForNotify, &KeyData); + // + // Leave critical section + // + gBS->RestoreTPL (OldTpl); + if (EFI_ERROR (Status)) { + break; + } + for (Link = GetFirstNode (NotifyList); + !IsNull (NotifyList, Link); + Link = GetNextNode (NotifyList, Link)) { + CurrentNotify = CR (Link, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY, + NotifyEntry, + VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE + ); + if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) { + CurrentNotify->KeyNotificationFn (&KeyData); + } + } + } +} + +/** + The user Entry Point for module VirtualKeyboard. The user code starts with + this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeVirtualKeyboard( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gVirtualKeyboardDriverBinding, + ImageHandle, + &gVirtualKeyboardComponentName, + &gVirtualKeyboardComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboard.h b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboard.h new file mode 100644 index 00000000..75b77c34 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboard.h @@ -0,0 +1,537 @@ +/** @file + +Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> +Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _VIRTUAL_KEYBOARD_H_ +#define _VIRTUAL_KEYBOARD_H_ + + +#include <Guid/StatusCodeDataTypeId.h> +#include <Protocol/DevicePath.h> +#include <Protocol/PlatformVirtualKeyboard.h> +#include <Protocol/SimpleTextIn.h> +#include <Protocol/SimpleTextInEx.h> + +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/IoLib.h> +#include <Library/PcdLib.h> +#include <Library/ReportStatusCodeLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiLib.h> + +// +// Driver Binding Externs +// +extern EFI_DRIVER_BINDING_PROTOCOL gVirtualKeyboardDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gVirtualKeyboardComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gVirtualKeyboardComponentName2; + + +// +// VIRTUAL Keyboard Defines +// +#define CHAR_SCANCODE 0xe0 +#define CHAR_ESC 0x1b + +#define KEYBOARD_TIMEOUT 65536 // 0.07s +#define KEYBOARD_WAITFORVALUE_TIMEOUT 1000000 // 1s +#define KEYBOARD_BAT_TIMEOUT 4000000 // 4s +#define KEYBOARD_TIMER_INTERVAL 500000 // 0.5s + +#define QUEUE_MAX_COUNT 32 + +#define KEYBOARD_SCAN_CODE_MAX_COUNT 32 + +// +// VIRTUAL Keyboard Device Structure +// +#define VIRTUAL_KEYBOARD_DEV_SIGNATURE SIGNATURE_32 ('V', 'K', 'B', 'D') +#define VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE SIGNATURE_32 ('v', 'k', 'c', 'n') + +typedef struct _VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY { + UINTN Signature; + EFI_KEY_DATA KeyData; + EFI_KEY_NOTIFY_FUNCTION KeyNotificationFn; + LIST_ENTRY NotifyEntry; +} VIRTUAL_KEYBOARD_CONSOLE_IN_EX_NOTIFY; + +typedef struct { + UINTN Front; + UINTN Rear; + EFI_KEY_DATA Buffer[QUEUE_MAX_COUNT]; +} SIMPLE_QUEUE; + +typedef struct { + UINT8 Buffer[KEYBOARD_SCAN_CODE_MAX_COUNT]; + UINTN Head; + UINTN Tail; +} SCAN_CODE_QUEUE; + +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + PLATFORM_VIRTUAL_KBD_PROTOCOL *PlatformVirtual; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL SimpleTextIn; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL SimpleTextInputEx; + + // + // Buffer storing EFI_KEY_DATA + // + SIMPLE_QUEUE Queue; + SIMPLE_QUEUE QueueForNotify; + + // + // Notification Function List + // + LIST_ENTRY NotifyList; + EFI_EVENT KeyNotifyProcessEvent; + EFI_EVENT TimerEvent; +} VIRTUAL_KEYBOARD_DEV; + +#define VIRTUAL_KEYBOARD_DEV_FROM_THIS(a) CR (a, VIRTUAL_KEYBOARD_DEV, SimpleTextIn, VIRTUAL_KEYBOARD_DEV_SIGNATURE) +#define TEXT_INPUT_EX_VIRTUAL_KEYBOARD_DEV_FROM_THIS(a) \ + CR (a, \ + VIRTUAL_KEYBOARD_DEV, \ + SimpleTextInputEx, \ + VIRTUAL_KEYBOARD_DEV_SIGNATURE \ + ) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gVirtualKeyboardDriverBinding; + +// +// Driver Binding Protocol functions +// + +/** + Check whether the driver supports this device. + + @param This The Udriver binding protocol. + @param Controller The controller handle to check. + @param RemainingDevicePath The remaining device path. + + @retval EFI_SUCCESS The driver supports this controller. + @retval other This device isn't supported. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts the device with this driver. + + @param This The driver binding instance. + @param Controller Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The controller is controlled by the driver. + @retval Other This controller cannot be started. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop the device handled by this driver. + + @param This The driver binding protocol. + @param Controller The controller to release. + @param NumberOfChildren The number of handles in ChildHandleBuffer. + @param ChildHandleBuffer The array of child handle. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + @retval Others Fail to uninstall protocols attached on the device. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +// +// Simple Text Input Protocol functions +// +/** + Reset the Keyboard and do BAT test for it, if (ExtendedVerification == TRUE) then do some extra keyboard validations. + + @param This Pointer of simple text Protocol. + @param ExtendedVerification Whether perform the extra validation of keyboard. True: perform; FALSE: skip. + + @retval EFI_SUCCESS The command byte is written successfully. + @retval EFI_DEVICE_ERROR Errors occurred during resetting keyboard. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardReset ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reset the input device and optionally run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardResetEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Set certain state for the input device. + + @param This Protocol instance pointer. + @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the + state for the input device. + + @retval EFI_SUCCESS The device state was set successfully. + @retval EFI_DEVICE_ERROR The device is not functioning correctly and could + not have the setting adjusted. + @retval EFI_UNSUPPORTED The device does not have the ability to set its state. + @retval EFI_INVALID_PARAMETER KeyToggleState is NULL. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardSetState ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_TOGGLE_STATE *KeyToggleState + ); + +/** + Register a notification function for a particular keystroke for the input device. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the keystroke + information data for the key that was pressed. + @param KeyNotificationFunction Points to the function to be called when the key + sequence is typed specified by KeyData. + @param NotifyHandle Points to the unique handle assigned to the registered notification. + + + @retval EFI_SUCCESS The notification function was registered successfully. + @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necessary data structures. + @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle is NULL. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardRegisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN EFI_KEY_DATA *KeyData, + IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + OUT VOID **NotifyHandle + ); + +/** + Remove a registered notification function from a particular keystroke. + + @param This Protocol instance pointer. + @param NotificationHandle The handle of the notification function being unregistered. + + @retval EFI_SUCCESS The notification function was unregistered successfully. + @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardUnregisterKeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + IN VOID *NotificationHandle + ); + +// +// Private worker functions +// +/** + Free keyboard notify list. + + @param ListHead The list head + + @retval EFI_SUCCESS Free the notify list successfully + @retval EFI_INVALID_PARAMETER ListHead is invalid. + +**/ +EFI_STATUS +VirtualKeyboardFreeNotifyList ( + IN OUT LIST_ENTRY *ListHead + ); + +/** + Check if key is registered. + + @param RegsiteredData A pointer to a buffer that is filled in with the keystroke + state data for the key that was registered. + @param InputData A pointer to a buffer that is filled in with the keystroke + state data for the key that was pressed. + + @retval TRUE Key be pressed matches a registered key. + @retval FALSE Match failed. + +**/ +BOOLEAN +IsKeyRegistered ( + IN EFI_KEY_DATA *RegsiteredData, + IN EFI_KEY_DATA *InputData + ); + +/** + Waiting on the keyboard event, if there's any key pressed by the user, signal the event + + @param Event The event that be signalled when any key has been struck. + @param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + +**/ +VOID +EFIAPI +VirtualKeyboardWaitForKey ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Waiting on the keyboard event, if there's any key pressed by the user, signal the event + + @param Event The event that be signalled when any key has been struck. + @param Context Pointer of the protocol EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + +**/ +VOID +EFIAPI +VirtualKeyboardWaitForKeyEx ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Timer event handler: read a series of key stroke from 8042 + and put them into memory key buffer. + It is registered as running under TPL_NOTIFY + + @param Event The timer event + @param Context A VIRTUAL_KEYBOARD_DEV pointer + +**/ +VOID +EFIAPI +VirtualKeyboardTimerHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Process key notify. + + @param Event Indicates the event that invoke this function. + @param Context Indicates the calling context. +**/ +VOID +EFIAPI +KeyNotifyProcessHandler ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Read out the scan code of the key that has just been stroked. + + @param This Pointer of simple text Protocol. + @param Key Pointer for store the key that read out. + + @retval EFI_SUCCESS The key is read out successfully. + @retval other The key reading failed. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardReadKeyStroke ( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ); + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existence of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param KeyData A pointer to a buffer that is filled in with the keystroke + state data for the key that was pressed. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data available. + @retval EFI_DEVICE_ERROR The keystroke information was not returned due to + hardware errors. + @retval EFI_INVALID_PARAMETER KeyData is NULL. + +**/ +EFI_STATUS +EFIAPI +VirtualKeyboardReadKeyStrokeEx ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + OUT EFI_KEY_DATA *KeyData + ); + +#endif /* _VIRTUAL_KEYBOARD_H_ */ diff --git a/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboardDxe.inf b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboardDxe.inf new file mode 100644 index 00000000..e74b3069 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/EmbeddedPkg/Drivers/VirtualKeyboardDxe/VirtualKeyboardDxe.inf @@ -0,0 +1,54 @@ +## @file +# Virtual Keyboard driver. +# +# Copyright (c) 2018, Intel Corporation. All rights reserved.<BR> +# Copyright (c) 2018, Linaro Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = VirtualKeyboardDxe + FILE_GUID = 88079b18-b42b-44aa-a6f2-b83911075e89 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeVirtualKeyboard + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 +# +# DRIVER_BINDING = gVirtualKeyboardDriverBinding +# COMPONENT_NAME = gVirtualKeyboardComponentName +# + +[Sources.common] + ComponentName.c + VirtualKeyboard.c + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + IoLib + ReportStatusCodeLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiDriverBindingProtocolGuid + gEfiSimpleTextInProtocolGuid + gEfiSimpleTextInputExProtocolGuid + gPlatformVirtualKeyboardProtocolGuid + +[Depex] + TRUE |