summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/VirtioSerialDxe/VirtioSerialPort.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/VirtioSerialPort.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/VirtioSerialPort.c')
-rw-r--r--OvmfPkg/VirtioSerialDxe/VirtioSerialPort.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/OvmfPkg/VirtioSerialDxe/VirtioSerialPort.c b/OvmfPkg/VirtioSerialDxe/VirtioSerialPort.c
new file mode 100644
index 0000000000..522b25e969
--- /dev/null
+++ b/OvmfPkg/VirtioSerialDxe/VirtioSerialPort.c
@@ -0,0 +1,465 @@
+/** @file
+
+ Driver for virtio-serial devices.
+
+ Helper functions to manage virtio serial ports.
+ Console ports will be registered as SerialIo UARTs.
+
+ 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/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/VirtioLib.h>
+
+#include "VirtioSerial.h"
+
+ACPI_HID_DEVICE_PATH mAcpiSerialDevNode = {
+ {
+ ACPI_DEVICE_PATH,
+ ACPI_DP,
+ {
+ (UINT8)(sizeof (ACPI_HID_DEVICE_PATH)),
+ (UINT8)((sizeof (ACPI_HID_DEVICE_PATH)) >> 8)
+ },
+ },
+ EISA_PNP_ID (0x0501),
+ 0
+};
+
+UART_DEVICE_PATH mUartDevNode = {
+ {
+ MESSAGING_DEVICE_PATH,
+ MSG_UART_DP,
+ {
+ (UINT8)(sizeof (UART_DEVICE_PATH)),
+ (UINT8)((sizeof (UART_DEVICE_PATH)) >> 8)
+ }
+ },
+ 0, // Reserved
+ 115200, // Speed
+ 8, 1, 1 // 8n1
+};
+
+STATIC
+UINT16
+PortRx (
+ IN UINT32 PortId
+ )
+{
+ ASSERT (PortId < MAX_PORTS);
+
+ if (PortId >= 1) {
+ return (UINT16)(VIRTIO_SERIAL_Q_RX_BASE + (PortId - 1) * 2);
+ }
+
+ return VIRTIO_SERIAL_Q_RX_PORT0;
+}
+
+STATIC
+UINT16
+PortTx (
+ IN UINT32 PortId
+ )
+{
+ ASSERT (PortId < MAX_PORTS);
+
+ if (PortId >= 1) {
+ return (UINT16)(VIRTIO_SERIAL_Q_TX_BASE + (PortId - 1) * 2);
+ }
+
+ return VIRTIO_SERIAL_Q_TX_PORT0;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialIoReset (
+ IN EFI_SERIAL_IO_PROTOCOL *This
+ )
+{
+ DEBUG ((DEBUG_VERBOSE, "%a:%d:\n", __func__, __LINE__));
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialIoSetAttributes (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ IN UINT64 BaudRate,
+ IN UINT32 ReceiveFifoDepth,
+ IN UINT32 Timeout,
+ IN EFI_PARITY_TYPE Parity,
+ IN UINT8 DataBits,
+ IN EFI_STOP_BITS_TYPE StopBits
+ )
+{
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a:%d: Rate %ld, Fifo %d, Bits %d\n",
+ __func__,
+ __LINE__,
+ BaudRate,
+ ReceiveFifoDepth,
+ DataBits
+ ));
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialIoSetControl (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ IN UINT32 Control
+ )
+{
+ DEBUG ((DEBUG_INFO, "%a:%d: Control 0x%x\n", __func__, __LINE__, Control));
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialIoGetControl (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ OUT UINT32 *Control
+ )
+{
+ DEBUG ((DEBUG_VERBOSE, "%a:%d: Control 0x%x\n", __func__, __LINE__, *Control));
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialIoWrite (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ VIRTIO_SERIAL_IO_PROTOCOL *SerialIo = (VIRTIO_SERIAL_IO_PROTOCOL *)This;
+ VIRTIO_SERIAL_PORT *Port = SerialIo->Dev->Ports + SerialIo->PortId;
+ UINT32 Length;
+ EFI_TPL OldTpl;
+
+ if (!Port->DeviceOpen) {
+ *BufferSize = 0;
+ return EFI_SUCCESS;
+ }
+
+ VirtioSerialRingClearTx (SerialIo->Dev, PortTx (SerialIo->PortId));
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (SerialIo->WriteOffset &&
+ (SerialIo->WriteOffset + *BufferSize > PORT_TX_BUFSIZE))
+ {
+ DEBUG ((DEBUG_VERBOSE, "%a:%d: WriteFlush %d\n", __func__, __LINE__, SerialIo->WriteOffset));
+ VirtioSerialRingSendBuffer (
+ SerialIo->Dev,
+ PortTx (SerialIo->PortId),
+ SerialIo->WriteBuffer,
+ SerialIo->WriteOffset,
+ TRUE
+ );
+ SerialIo->WriteOffset = 0;
+ }
+
+ Length = MIN ((UINT32)(*BufferSize), PORT_TX_BUFSIZE - SerialIo->WriteOffset);
+ CopyMem (SerialIo->WriteBuffer + SerialIo->WriteOffset, Buffer, Length);
+ SerialIo->WriteOffset += Length;
+ *BufferSize = Length;
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialIoRead (
+ IN EFI_SERIAL_IO_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ VIRTIO_SERIAL_IO_PROTOCOL *SerialIo = (VIRTIO_SERIAL_IO_PROTOCOL *)This;
+ VIRTIO_SERIAL_PORT *Port = SerialIo->Dev->Ports + SerialIo->PortId;
+ BOOLEAN HasData;
+ UINT32 Length;
+ EFI_TPL OldTpl;
+
+ if (!Port->DeviceOpen) {
+ goto NoData;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (SerialIo->WriteOffset) {
+ DEBUG ((DEBUG_VERBOSE, "%a:%d: WriteFlush %d\n", __func__, __LINE__, SerialIo->WriteOffset));
+ VirtioSerialRingSendBuffer (
+ SerialIo->Dev,
+ PortTx (SerialIo->PortId),
+ SerialIo->WriteBuffer,
+ SerialIo->WriteOffset,
+ TRUE
+ );
+ SerialIo->WriteOffset = 0;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ if (SerialIo->ReadOffset == SerialIo->ReadSize) {
+ HasData = VirtioSerialRingGetBuffer (
+ SerialIo->Dev,
+ PortRx (SerialIo->PortId),
+ &SerialIo->ReadBuffer,
+ &SerialIo->ReadSize
+ );
+ if (!HasData) {
+ goto NoData;
+ }
+
+ SerialIo->ReadOffset = 0;
+ }
+
+ if (SerialIo->ReadOffset < SerialIo->ReadSize) {
+ Length = SerialIo->ReadSize - SerialIo->ReadOffset;
+ if (Length > *BufferSize) {
+ Length = (UINT32)(*BufferSize);
+ }
+
+ CopyMem (Buffer, SerialIo->ReadBuffer + SerialIo->ReadOffset, Length);
+ SerialIo->ReadOffset += Length;
+ *BufferSize = Length;
+ return EFI_SUCCESS;
+ }
+
+NoData:
+ *BufferSize = 0;
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioSerialIoInit (
+ IN OUT VIRTIO_SERIAL_DEV *Dev,
+ IN UINT32 PortId
+ )
+{
+ VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
+ VIRTIO_SERIAL_IO_PROTOCOL *SerialIo;
+ EFI_STATUS Status;
+
+ SerialIo = (VIRTIO_SERIAL_IO_PROTOCOL *)AllocateZeroPool (sizeof *SerialIo);
+ Port->SerialIo = SerialIo;
+
+ SerialIo->SerialIo.Revision = EFI_SERIAL_IO_PROTOCOL_REVISION;
+ SerialIo->SerialIo.Reset = VirtioSerialIoReset;
+ SerialIo->SerialIo.SetAttributes = VirtioSerialIoSetAttributes;
+ SerialIo->SerialIo.SetControl = VirtioSerialIoSetControl;
+ SerialIo->SerialIo.GetControl = VirtioSerialIoGetControl;
+ SerialIo->SerialIo.Write = VirtioSerialIoWrite;
+ SerialIo->SerialIo.Read = VirtioSerialIoRead;
+ SerialIo->SerialIo.Mode = &SerialIo->SerialIoMode;
+ SerialIo->Dev = Dev;
+ SerialIo->PortId = PortId;
+
+ SerialIo->DevicePath = DuplicateDevicePath (Dev->DevicePath);
+ mAcpiSerialDevNode.UID = PortId;
+ SerialIo->DevicePath = AppendDevicePathNode (
+ SerialIo->DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&mAcpiSerialDevNode
+ );
+ SerialIo->DevicePath = AppendDevicePathNode (
+ SerialIo->DevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *)&mUartDevNode
+ );
+
+ LogDevicePath (DEBUG_INFO, __func__, L"UART", SerialIo->DevicePath);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &SerialIo->DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ SerialIo->DevicePath,
+ &gEfiSerialIoProtocolGuid,
+ &SerialIo->SerialIo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "%a:%d: ERROR: %r\n", __func__, __LINE__, Status));
+ goto FreeSerialIo;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dev->DeviceHandle,
+ &gVirtioDeviceProtocolGuid,
+ (VOID **)&Dev->VirtIo,
+ Dev->DriverBindingHandle,
+ SerialIo->DeviceHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO, "%a:%d: ERROR: %r\n", __func__, __LINE__, Status));
+ goto UninstallProtocol;
+ }
+
+ return EFI_SUCCESS;
+
+UninstallProtocol:
+ gBS->UninstallMultipleProtocolInterfaces (
+ SerialIo->DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ SerialIo->DevicePath,
+ &gEfiSerialIoProtocolGuid,
+ &SerialIo->SerialIo,
+ NULL
+ );
+
+FreeSerialIo:
+ FreePool (Port->SerialIo);
+ Port->SerialIo = NULL;
+ return Status;
+}
+
+STATIC
+VOID
+EFIAPI
+VirtioSerialIoUninit (
+ VIRTIO_SERIAL_IO_PROTOCOL *SerialIo
+ )
+{
+ VIRTIO_SERIAL_DEV *Dev = SerialIo->Dev;
+ VIRTIO_SERIAL_PORT *Port = Dev->Ports + SerialIo->PortId;
+
+ DEBUG ((DEBUG_INFO, "%a:%d: %s\n", __func__, __LINE__, Port->Name));
+
+ gBS->CloseProtocol (
+ Dev->DeviceHandle,
+ &gVirtioDeviceProtocolGuid,
+ Dev->DriverBindingHandle,
+ SerialIo->DeviceHandle
+ );
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ SerialIo->DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ SerialIo->DevicePath,
+ &gEfiSerialIoProtocolGuid,
+ &SerialIo->SerialIo,
+ NULL
+ );
+
+ FreePool (SerialIo);
+ Port->SerialIo = NULL;
+}
+
+EFI_STATUS
+EFIAPI
+VirtioSerialPortAdd (
+ IN OUT VIRTIO_SERIAL_DEV *Dev,
+ IN UINT32 PortId
+ )
+{
+ VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
+ EFI_STATUS Status;
+
+ if (Port->Ready) {
+ return EFI_SUCCESS;
+ }
+
+ Status = VirtioSerialInitRing (Dev, PortRx (PortId), PORT_RX_BUFSIZE);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ Status = VirtioSerialInitRing (Dev, PortTx (PortId), PORT_TX_BUFSIZE);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ UnicodeSPrint (Port->Name, sizeof (Port->Name), L"Port #%d", PortId);
+ VirtioSerialRingFillRx (Dev, PortRx (PortId));
+ Port->Ready = TRUE;
+
+ return EFI_SUCCESS;
+
+Failed:
+ VirtioSerialUninitRing (Dev, PortRx (PortId));
+ return Status;
+}
+
+VOID
+EFIAPI
+VirtioSerialPortSetConsole (
+ IN OUT VIRTIO_SERIAL_DEV *Dev,
+ IN UINT32 PortId
+ )
+{
+ VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
+
+ Port->Console = TRUE;
+ UnicodeSPrint (Port->Name, sizeof (Port->Name), L"Console #%d", PortId);
+ VirtioSerialIoInit (Dev, PortId);
+}
+
+VOID
+EFIAPI
+VirtioSerialPortSetName (
+ IN OUT VIRTIO_SERIAL_DEV *Dev,
+ IN UINT32 PortId,
+ IN UINT8 *Name
+ )
+{
+ VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
+
+ DEBUG ((DEBUG_INFO, "%a:%d: \"%a\"\n", __func__, __LINE__, Name));
+ UnicodeSPrint (Port->Name, sizeof (Port->Name), L"NamedPort #%d (%a)", PortId, Name);
+}
+
+VOID
+EFIAPI
+VirtioSerialPortSetDeviceOpen (
+ IN OUT VIRTIO_SERIAL_DEV *Dev,
+ IN UINT32 PortId,
+ IN UINT16 Value
+ )
+{
+ VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
+
+ Port->DeviceOpen = (BOOLEAN)Value;
+ if (Port->DeviceOpen) {
+ VirtioSerialTxControl (Dev, PortId, VIRTIO_SERIAL_PORT_OPEN, 1);
+ }
+}
+
+VOID
+EFIAPI
+VirtioSerialPortRemove (
+ IN OUT VIRTIO_SERIAL_DEV *Dev,
+ IN UINT32 PortId
+ )
+{
+ VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
+
+ if (!Port->Ready) {
+ return;
+ }
+
+ if (Port->SerialIo) {
+ VirtioSerialIoUninit (Port->SerialIo);
+ Port->SerialIo = NULL;
+ }
+
+ VirtioSerialUninitRing (Dev, PortRx (PortId));
+ VirtioSerialUninitRing (Dev, PortTx (PortId));
+ Port->Ready = FALSE;
+}