diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2023-05-04 15:11:59 +0200 |
---|---|---|
committer | mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> | 2023-05-04 14:26:58 +0000 |
commit | 4d1452c59979b9fcc762db692688b5b3b6573106 (patch) | |
tree | 49e68515d4d4a85653edddf2c0e1ab9cbc0c2134 /OvmfPkg/VirtioSerialDxe/VirtioSerialPort.c | |
parent | 1694b0051138dc121740e47896d165b8b12382c6 (diff) | |
download | edk2-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.c | 465 |
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;
+}
|