mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-02 13:03:28 +01:00
807 lines
21 KiB
C
807 lines
21 KiB
C
|
/** @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;
|
||
|
|
||
|
Control.Id = Id;
|
||
|
Control.Event = Event;
|
||
|
Control.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;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
);
|
||
|
}
|