/** @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 #include #include #include #include #include #include #include #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_NOTIFY); 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_NOTIFY); 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; }