/** @file | |
* Virtio FDT client protocol driver for virtio,mmio DT node | |
* | |
* Copyright (c) 2014 - 2016, Linaro Ltd. All rights reserved.<BR> | |
* | |
* SPDX-License-Identifier: BSD-2-Clause-Patent | |
* | |
**/ | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/DevicePathLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiDriverEntryPoint.h> | |
#include <Library/VirtioMmioDeviceLib.h> | |
#include <Guid/VirtioMmioTransport.h> | |
#include <Protocol/FdtClient.h> | |
#pragma pack (1) | |
typedef struct { | |
VENDOR_DEVICE_PATH Vendor; | |
UINT64 PhysBase; | |
EFI_DEVICE_PATH_PROTOCOL End; | |
} VIRTIO_TRANSPORT_DEVICE_PATH; | |
#pragma pack () | |
EFI_STATUS | |
EFIAPI | |
InitializeVirtioFdtDxe ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status, FindNodeStatus; | |
FDT_CLIENT_PROTOCOL *FdtClient; | |
INT32 Node; | |
CONST UINT64 *Reg; | |
UINT32 RegSize; | |
VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath; | |
EFI_HANDLE Handle; | |
UINT64 RegBase; | |
Status = gBS->LocateProtocol ( | |
&gFdtClientProtocolGuid, | |
NULL, | |
(VOID **)&FdtClient | |
); | |
ASSERT_EFI_ERROR (Status); | |
for (FindNodeStatus = FdtClient->FindCompatibleNode ( | |
FdtClient, | |
"virtio,mmio", | |
&Node | |
); | |
!EFI_ERROR (FindNodeStatus); | |
FindNodeStatus = FdtClient->FindNextCompatibleNode ( | |
FdtClient, | |
"virtio,mmio", | |
Node, | |
&Node | |
)) | |
{ | |
Status = FdtClient->GetNodeProperty ( | |
FdtClient, | |
Node, | |
"reg", | |
(CONST VOID **)&Reg, | |
&RegSize | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: GetNodeProperty () failed (Status == %r)\n", | |
__func__, | |
Status | |
)); | |
continue; | |
} | |
ASSERT (RegSize == 16); | |
// | |
// Create a unique device path for this transport on the fly | |
// | |
RegBase = SwapBytes64 (*Reg); | |
DevicePath = (VIRTIO_TRANSPORT_DEVICE_PATH *)CreateDeviceNode ( | |
HARDWARE_DEVICE_PATH, | |
HW_VENDOR_DP, | |
sizeof (VIRTIO_TRANSPORT_DEVICE_PATH) | |
); | |
if (DevicePath == NULL) { | |
DEBUG ((DEBUG_ERROR, "%a: Out of memory\n", __func__)); | |
continue; | |
} | |
CopyGuid (&DevicePath->Vendor.Guid, &gVirtioMmioTransportGuid); | |
DevicePath->PhysBase = RegBase; | |
SetDevicePathNodeLength ( | |
&DevicePath->Vendor, | |
sizeof (*DevicePath) - sizeof (DevicePath->End) | |
); | |
SetDevicePathEndNode (&DevicePath->End); | |
Handle = NULL; | |
Status = gBS->InstallProtocolInterface ( | |
&Handle, | |
&gEfiDevicePathProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
DevicePath | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Failed to install the EFI_DEVICE_PATH " | |
"protocol on a new handle (Status == %r)\n", | |
__func__, | |
Status | |
)); | |
FreePool (DevicePath); | |
continue; | |
} | |
Status = VirtioMmioInstallDevice (RegBase, Handle); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Failed to install VirtIO transport @ 0x%Lx " | |
"on handle %p (Status == %r)\n", | |
__func__, | |
RegBase, | |
Handle, | |
Status | |
)); | |
Status = gBS->UninstallProtocolInterface ( | |
Handle, | |
&gEfiDevicePathProtocolGuid, | |
DevicePath | |
); | |
ASSERT_EFI_ERROR (Status); | |
FreePool (DevicePath); | |
continue; | |
} | |
} | |
if (EFI_ERROR (FindNodeStatus) && (FindNodeStatus != EFI_NOT_FOUND)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Error occurred while iterating DT nodes " | |
"(FindNodeStatus == %r)\n", | |
__func__, | |
FindNodeStatus | |
)); | |
} | |
return EFI_SUCCESS; | |
} |