summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/VirtioSerialDxe/VirtioSerial.c
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2023-05-04 15:11:59 +0200
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2023-05-04 14:26:58 +0000
commit4d1452c59979b9fcc762db692688b5b3b6573106 (patch)
tree49e68515d4d4a85653edddf2c0e1ab9cbc0c2134 /OvmfPkg/VirtioSerialDxe/VirtioSerial.c
parent1694b0051138dc121740e47896d165b8b12382c6 (diff)
downloadedk2-4d1452c59979b9fcc762db692688b5b3b6573106.tar.gz
edk2-4d1452c59979b9fcc762db692688b5b3b6573106.tar.bz2
edk2-4d1452c59979b9fcc762db692688b5b3b6573106.zip
OvmfPkg/VirtioSerialDxe: add driver
Add a driver for the virtio serial device. The virtio serial device also known as virtio console device because initially it had only support for a single tty, intended to be used as console. Support for multiple streams and named data ports has been added later on. The driver supports tty ports only, they are registered as SerialIo UART in the system. Named ports are detected and logged, but not exposed as devices. They are usually used by guest agents to communicate with the host. It's not clear whenever it makes sense for the firmware to run such agents and if so which efi protocol could be to expose the ports. So leaving that for another day. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'OvmfPkg/VirtioSerialDxe/VirtioSerial.c')
-rw-r--r--OvmfPkg/VirtioSerialDxe/VirtioSerial.c808
1 files changed, 808 insertions, 0 deletions
diff --git a/OvmfPkg/VirtioSerialDxe/VirtioSerial.c b/OvmfPkg/VirtioSerialDxe/VirtioSerial.c
new file mode 100644
index 0000000000..bfb2b324ea
--- /dev/null
+++ b/OvmfPkg/VirtioSerialDxe/VirtioSerial.c
@@ -0,0 +1,808 @@
+/** @file
+
+ Driver for virtio-serial devices.
+
+ The virtio serial device also known as virtio console device because
+ initially it had only support for a single tty, intended to be used
+ as console. Support for multiple streams and named data ports has
+ been added later on.
+
+ https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-2900003
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/VirtioLib.h>
+
+#include "VirtioSerial.h"
+
+STATIC LIST_ENTRY mVirtioSerialList;
+
+STATIC CONST CHAR8 *EventNames[] = {
+ [VIRTIO_SERIAL_DEVICE_READY] = "device-ready",
+ [VIRTIO_SERIAL_DEVICE_ADD] = "device-add",
+ [VIRTIO_SERIAL_DEVICE_REMOVE] = "device-remove",
+ [VIRTIO_SERIAL_PORT_READY] = "port-ready",
+ [VIRTIO_SERIAL_CONSOLE_PORT] = "console-port",
+ [VIRTIO_SERIAL_RESIZE] = "resize",
+ [VIRTIO_SERIAL_PORT_OPEN] = "port-open",
+ [VIRTIO_SERIAL_PORT_NAME] = "port-name",
+};
+
+VOID
+EFIAPI
+LogDevicePath (
+ UINT32 Level,
+ const CHAR8 *Func,
+ CHAR16 *Note,
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ CHAR16 *Str;
+
+ Str = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
+ if (!Str) {
+ DEBUG ((DEBUG_INFO, "ConvertDevicePathToText failed\n"));
+ return;
+ }
+
+ DEBUG ((Level, "%a: %s%s%s\n", Func, Note ? Note : L"", Note ? L": " : L"", Str));
+ FreePool (Str);
+}
+
+EFI_STATUS
+EFIAPI
+VirtioSerialTxControl (
+ IN OUT VIRTIO_SERIAL_DEV *Dev,
+ IN UINT32 Id,
+ IN UINT16 Event,
+ IN UINT16 Value
+ )
+{
+ VIRTIO_SERIAL_CONTROL Control = {
+ .Id = Id,
+ .Event = Event,
+ .Value = Value,
+ };
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a:%d: >>> event %a, port-id %d, value %d\n",
+ __func__,
+ __LINE__,
+ EventNames[Control.Event],
+ Control.Id,
+ Control.Value
+ ));
+
+ VirtioSerialRingClearTx (Dev, VIRTIO_SERIAL_Q_TX_CTRL);
+ return VirtioSerialRingSendBuffer (Dev, VIRTIO_SERIAL_Q_TX_CTRL, &Control, sizeof (Control), TRUE);
+}
+
+STATIC
+VOID
+EFIAPI
+VirtioSerialRxControl (
+ IN OUT VIRTIO_SERIAL_DEV *Dev
+ )
+{
+ UINT8 Data[CTRL_RX_BUFSIZE+1];
+ UINT32 DataSize;
+ VIRTIO_SERIAL_CONTROL Control;
+ EFI_STATUS Status;
+ BOOLEAN HasData;
+ UINT16 Ready;
+
+ for ( ; ;) {
+ HasData = VirtioSerialRingGetBuffer (Dev, VIRTIO_SERIAL_Q_RX_CTRL, Data, &DataSize);
+ if (!HasData) {
+ return;
+ }
+
+ if (DataSize < sizeof (Control)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%d: length mismatch: %d != %d\n",
+ __func__,
+ __LINE__,
+ DataSize,
+ sizeof (Control)
+ ));
+ continue;
+ }
+
+ CopyMem (&Control, Data, sizeof (Control));
+
+ if (Control.Event < ARRAY_SIZE (EventNames)) {
+ DEBUG ((
+ DEBUG_INFO,
+ "%a:%d: <<< event %a, port-id %d, value %d\n",
+ __func__,
+ __LINE__,
+ EventNames[Control.Event],
+ Control.Id,
+ Control.Value
+ ));
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%d: unknown event: %d\n",
+ __func__,
+ __LINE__,
+ Control.Event
+ ));
+ }
+
+ switch (Control.Event) {
+ case VIRTIO_SERIAL_DEVICE_ADD:
+ if (Control.Id < MAX_PORTS) {
+ Status = VirtioSerialPortAdd (Dev, Control.Id);
+ Ready = (Status == EFI_SUCCESS) ? 1 : 0;
+ } else {
+ Ready = 0;
+ }
+
+ VirtioSerialTxControl (Dev, Control.Id, VIRTIO_SERIAL_PORT_READY, Ready);
+ if (Ready) {
+ Dev->NumPorts++;
+ }
+
+ break;
+ case VIRTIO_SERIAL_DEVICE_REMOVE:
+ if (Control.Id < MAX_PORTS) {
+ VirtioSerialPortRemove (Dev, Control.Id);
+ }
+
+ break;
+ case VIRTIO_SERIAL_CONSOLE_PORT:
+ if (Control.Id < MAX_PORTS) {
+ VirtioSerialPortSetConsole (Dev, Control.Id);
+ Dev->NumConsoles++;
+ }
+
+ break;
+ case VIRTIO_SERIAL_PORT_NAME:
+ if (Control.Id < MAX_PORTS) {
+ Data[DataSize] = 0;
+ VirtioSerialPortSetName (Dev, Control.Id, Data + sizeof (Control));
+ Dev->NumNamedPorts++;
+ }
+
+ break;
+ case VIRTIO_SERIAL_PORT_OPEN:
+ if (Control.Id < MAX_PORTS) {
+ VirtioSerialPortSetDeviceOpen (Dev, Control.Id, Control.Value);
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+STATIC
+VOID
+EFIAPI
+VirtioSerialTimer (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ VIRTIO_SERIAL_DEV *Dev = Context;
+
+ VirtioSerialRxControl (Dev);
+}
+
+STATIC
+VOID
+EFIAPI
+VirtioSerialUninitAllRings (
+ IN OUT VIRTIO_SERIAL_DEV *Dev
+ )
+{
+ UINT16 Index;
+
+ for (Index = 0; Index < MAX_RINGS; Index++) {
+ VirtioSerialUninitRing (Dev, Index);
+ }
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialInit (
+ IN OUT VIRTIO_SERIAL_DEV *Dev
+ )
+{
+ UINT8 NextDevStat;
+ EFI_STATUS Status;
+ UINT64 Features;
+ UINTN Retries;
+
+ //
+ // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.
+ //
+ NextDevStat = 0; // step 1 -- reset device
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // Set Page Size - MMIO VirtIo Specific
+ //
+ Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // step 4a -- retrieve and validate features
+ //
+ Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ Features &= (VIRTIO_F_VERSION_1 |
+ VIRTIO_F_IOMMU_PLATFORM |
+ VIRTIO_SERIAL_F_MULTIPORT);
+
+ //
+ // In virtio-1.0, feature negotiation is expected to complete before queue
+ // discovery, and the device can also reject the selected set of features.
+ //
+ if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
+ Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a:%d: features ok:%a%a%a\n",
+ __func__,
+ __LINE__,
+ (Features & VIRTIO_F_VERSION_1) ? " v1.0" : "",
+ (Features & VIRTIO_F_IOMMU_PLATFORM) ? " iommu" : "",
+ (Features & VIRTIO_SERIAL_F_MULTIPORT) ? " multiport" : ""
+ ));
+
+ if (Features & VIRTIO_SERIAL_F_MULTIPORT) {
+ Dev->VirtIo->ReadDevice (
+ Dev->VirtIo,
+ OFFSET_OF (VIRTIO_SERIAL_CONFIG, MaxPorts),
+ sizeof (Dev->Config.MaxPorts),
+ sizeof (Dev->Config.MaxPorts),
+ &Dev->Config.MaxPorts
+ );
+ DEBUG ((
+ DEBUG_INFO,
+ "%a:%d: max device ports: %d\n",
+ __func__,
+ __LINE__,
+ Dev->Config.MaxPorts
+ ));
+ }
+
+ Status = VirtioSerialInitRing (Dev, VIRTIO_SERIAL_Q_RX_CTRL, CTRL_RX_BUFSIZE);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ Status = VirtioSerialInitRing (Dev, VIRTIO_SERIAL_Q_TX_CTRL, CTRL_TX_BUFSIZE);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // step 5 -- Report understood features and guest-tuneables.
+ //
+ if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
+ Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);
+ Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ }
+
+ //
+ // step 6 -- initialization complete
+ //
+ NextDevStat |= VSTAT_DRIVER_OK;
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ VirtioSerialRingFillRx (Dev, VIRTIO_SERIAL_Q_RX_CTRL);
+ VirtioSerialTxControl (Dev, 0, VIRTIO_SERIAL_DEVICE_READY, 1);
+ for (Retries = 0; Retries < 100; Retries++) {
+ gBS->Stall (1000);
+ VirtioSerialRxControl (Dev);
+ if (Dev->NumPorts && (Dev->NumConsoles + Dev->NumNamedPorts == Dev->NumPorts)) {
+ // port discovery complete
+ break;
+ }
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ VirtioSerialTimer,
+ Dev,
+ &Dev->Timer
+ );
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ Status = gBS->SetTimer (
+ Dev->Timer,
+ TimerPeriodic,
+ EFI_TIMER_PERIOD_MILLISECONDS (10)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a:%d: OK, %d consoles, %d named ports\n",
+ __func__,
+ __LINE__,
+ Dev->NumConsoles,
+ Dev->NumNamedPorts
+ ));
+ return EFI_SUCCESS;
+
+Failed:
+ VirtioSerialUninitAllRings (Dev);
+
+ //
+ // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device
+ // Status. VirtIo access failure here should not mask the original error.
+ //
+ NextDevStat |= VSTAT_FAILED;
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+
+ DEBUG ((DEBUG_INFO, "%a:%d: ERROR: %r\n", __func__, __LINE__, Status));
+ return Status; // reached only via Failed above
+}
+
+STATIC
+VOID
+EFIAPI
+VirtioSerialUninit (
+ IN OUT VIRTIO_SERIAL_DEV *Dev
+ )
+{
+ UINT32 PortId;
+
+ gBS->CloseEvent (Dev->Timer);
+
+ //
+ // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
+ // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
+ // the old comms area.
+ //
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+
+ for (PortId = 0; PortId < MAX_PORTS; PortId++) {
+ VirtioSerialPortRemove (Dev, PortId);
+ }
+
+ VirtioSerialUninitAllRings (Dev);
+}
+
+//
+// Event notification function enqueued by ExitBootServices().
+//
+
+STATIC
+VOID
+EFIAPI
+VirtioSerialExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ VIRTIO_SERIAL_DEV *Dev;
+
+ DEBUG ((DEBUG_INFO, "%a: Context=0x%p\n", __func__, Context));
+ //
+ // Reset the device. This causes the hypervisor to forget about the virtio
+ // ring.
+ //
+ // We allocated said ring in EfiBootServicesData type memory, and code
+ // executing after ExitBootServices() is permitted to overwrite it.
+ //
+ Dev = Context;
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+}
+
+STATIC
+VIRTIO_SERIAL_DEV *
+VirtioSerialFind (
+ EFI_HANDLE DeviceHandle
+ )
+{
+ VIRTIO_SERIAL_DEV *Dev;
+ LIST_ENTRY *Entry;
+
+ BASE_LIST_FOR_EACH (Entry, &mVirtioSerialList) {
+ Dev = CR (Entry, VIRTIO_SERIAL_DEV, Link, VIRTIO_SERIAL_SIG);
+ if (DeviceHandle == Dev->DeviceHandle) {
+ return Dev;
+ }
+ }
+ return NULL;
+}
+
+//
+// Probe, start and stop functions of this driver, called by the DXE core for
+// specific devices.
+//
+// The following specifications document these interfaces:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
+// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
+//
+// The implementation follows:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01
+// - 5.1.3.4 OpenProtocol() and CloseProtocol()
+// - UEFI Spec 2.3.1 + Errata C
+// - 6.3 Protocol Handler Services
+//
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;
+
+ //
+ // Attempt to open the device with the VirtIo set of interfaces. On success,
+ // the protocol is "instantiated" for the VirtIo device. Covers duplicate
+ // open attempts (EFI_ALREADY_STARTED).
+ //
+ Status = gBS->OpenProtocol (
+ DeviceHandle, // candidate device
+ &gVirtioDeviceProtocolGuid, // for generic VirtIo access
+ (VOID **)&VirtIo, // handle to instantiate
+ This->DriverBindingHandle, // requestor driver identity
+ DeviceHandle, // ControllerHandle, according to
+ // the UEFI Driver Model
+ EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to
+ // the device; to be released
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_CONSOLE) {
+ Status = EFI_UNSUPPORTED;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a:%d: subsystem %d -> %r\n", __func__, __LINE__, VirtIo->SubSystemDeviceId, Status));
+
+ //
+ // We needed VirtIo access only transitorily, to see whether we support the
+ // device or not.
+ //
+ gBS->CloseProtocol (
+ DeviceHandle,
+ &gVirtioDeviceProtocolGuid,
+ This->DriverBindingHandle,
+ DeviceHandle
+ );
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ VIRTIO_SERIAL_DEV *Dev;
+ EFI_STATUS Status;
+
+ Dev = (VIRTIO_SERIAL_DEV *)AllocateZeroPool (sizeof *Dev);
+ if (Dev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->OpenProtocol (
+ DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **)&Dev->DevicePath,
+ This->DriverBindingHandle,
+ DeviceHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeVirtioSerial;
+ }
+
+ Status = gBS->OpenProtocol (
+ DeviceHandle,
+ &gVirtioDeviceProtocolGuid,
+ (VOID **)&Dev->VirtIo,
+ This->DriverBindingHandle,
+ DeviceHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeVirtioSerial;
+ }
+
+ LogDevicePath (DEBUG_INFO, __func__, L"Dev", Dev->DevicePath);
+
+ //
+ // VirtIo access granted, configure virtio-serial device.
+ //
+ Dev->Signature = VIRTIO_SERIAL_SIG;
+ Dev->DriverBindingHandle = This->DriverBindingHandle;
+ Dev->DeviceHandle = DeviceHandle;
+ Status = VirtioSerialInit (Dev);
+ if (EFI_ERROR (Status)) {
+ goto CloseVirtIo;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_SIGNAL_EXIT_BOOT_SERVICES,
+ TPL_CALLBACK,
+ &VirtioSerialExitBoot,
+ Dev,
+ &Dev->ExitBoot
+ );
+ if (EFI_ERROR (Status)) {
+ goto UninitDev;
+ }
+
+ InsertTailList (&mVirtioSerialList, &(Dev->Link));
+ return EFI_SUCCESS;
+
+UninitDev:
+ VirtioSerialUninit (Dev);
+
+CloseVirtIo:
+ gBS->CloseProtocol (
+ DeviceHandle,
+ &gVirtioDeviceProtocolGuid,
+ This->DriverBindingHandle,
+ DeviceHandle
+ );
+
+FreeVirtioSerial:
+ FreePool (Dev);
+
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ VIRTIO_SERIAL_DEV *Dev;
+ UINT32 PortId;
+ UINT32 Child;
+
+ Dev = VirtioSerialFind (DeviceHandle);
+ if (!Dev) {
+ return EFI_SUCCESS;
+ }
+
+ if (NumberOfChildren) {
+ for (Child = 0; Child < NumberOfChildren; Child++) {
+ DEBUG ((DEBUG_INFO, "%a:%d: child handle 0x%x\n", __func__, __LINE__, ChildHandleBuffer[Child]));
+ for (PortId = 0; PortId < MAX_PORTS; PortId++) {
+ if (Dev->Ports[PortId].Ready &&
+ Dev->Ports[PortId].SerialIo &&
+ (Dev->Ports[PortId].SerialIo->DeviceHandle == ChildHandleBuffer[Child]))
+ {
+ VirtioSerialPortRemove (Dev, PortId);
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a:%d: controller handle 0x%x\n", __func__, __LINE__, DeviceHandle));
+
+ gBS->CloseEvent (Dev->ExitBoot);
+
+ VirtioSerialUninit (Dev);
+
+ gBS->CloseProtocol (
+ DeviceHandle,
+ &gVirtioDeviceProtocolGuid,
+ This->DriverBindingHandle,
+ DeviceHandle
+ );
+
+ RemoveEntryList (&(Dev->Link));
+ ZeroMem (Dev, sizeof (*Dev));
+ FreePool (Dev);
+
+ return EFI_SUCCESS;
+}
+
+//
+// The static object that groups the Supported() (ie. probe), Start() and
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
+// C, 10.1 EFI Driver Binding Protocol.
+//
+STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
+ &VirtioSerialDriverBindingSupported,
+ &VirtioSerialDriverBindingStart,
+ &VirtioSerialDriverBindingStop,
+ 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
+ NULL, // ImageHandle, to be overwritten by
+ // EfiLibInstallDriverBindingComponentName2() in VirtioSerialEntryPoint()
+ NULL // DriverBindingHandle, ditto
+};
+
+//
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
+// in English, for display on standard console devices. This is recommended for
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
+//
+
+STATIC
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+ { "eng;en", L"Virtio Serial Driver" },
+ { NULL, NULL }
+};
+
+STATIC
+EFI_UNICODE_STRING_TABLE mDeviceNameTable[] = {
+ { "eng;en", L"Virtio Serial Device" },
+ { NULL, NULL }
+};
+
+STATIC
+EFI_UNICODE_STRING_TABLE mPortNameTable[] = {
+ { "eng;en", L"Virtio Serial Port" },
+ { NULL, NULL }
+};
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL gComponentName;
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gComponentName) // Iso639Language
+ );
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialGetDeviceName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_HANDLE ChildHandle,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_UNICODE_STRING_TABLE *Table;
+ VIRTIO_SERIAL_DEV *Dev;
+ UINT32 PortId;
+
+ Dev = VirtioSerialFind (DeviceHandle);
+ if (!Dev) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (ChildHandle) {
+ for (PortId = 0; PortId < MAX_PORTS; PortId++) {
+ if (Dev->Ports[PortId].Ready &&
+ Dev->Ports[PortId].SerialIo &&
+ (Dev->Ports[PortId].SerialIo->DeviceHandle == ChildHandle))
+ {
+ *ControllerName = Dev->Ports[PortId].Name;
+ return EFI_SUCCESS;
+ }
+ }
+
+ Table = mPortNameTable;
+ } else {
+ Table = mDeviceNameTable;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ Table,
+ ControllerName,
+ (BOOLEAN)(This == &gComponentName)
+ );
+}
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
+ &VirtioSerialGetDriverName,
+ &VirtioSerialGetDeviceName,
+ "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+STATIC
+EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&VirtioSerialGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&VirtioSerialGetDeviceName,
+ "en" // SupportedLanguages, RFC 4646 language codes
+};
+
+//
+// Entry point of this driver.
+//
+EFI_STATUS
+EFIAPI
+VirtioSerialEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ InitializeListHead (&mVirtioSerialList);
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDriverBinding,
+ ImageHandle,
+ &gComponentName,
+ &gComponentName2
+ );
+}