/** @file
* FDT client driver
*
* Copyright (c) 2016, Linaro Ltd. All rights reserved.
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
STATIC VOID *mDeviceTreeBase;
STATIC
EFI_STATUS
EFIAPI
GetNodeProperty (
IN FDT_CLIENT_PROTOCOL *This,
IN INT32 Node,
IN CONST CHAR8 *PropertyName,
OUT CONST VOID **Prop,
OUT UINT32 *PropSize OPTIONAL
)
{
INT32 Len;
ASSERT (mDeviceTreeBase != NULL);
ASSERT (Prop != NULL);
*Prop = fdt_getprop (mDeviceTreeBase, Node, PropertyName, &Len);
if (*Prop == NULL) {
return EFI_NOT_FOUND;
}
if (PropSize != NULL) {
*PropSize = Len;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
SetNodeProperty (
IN FDT_CLIENT_PROTOCOL *This,
IN INT32 Node,
IN CONST CHAR8 *PropertyName,
IN CONST VOID *Prop,
IN UINT32 PropSize
)
{
INT32 Ret;
ASSERT (mDeviceTreeBase != NULL);
Ret = fdt_setprop (mDeviceTreeBase, Node, PropertyName, Prop, PropSize);
if (Ret != 0) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
STATIC
BOOLEAN
IsNodeEnabled (
INT32 Node
)
{
CONST CHAR8 *NodeStatus;
INT32 Len;
//
// A missing status property implies 'ok' so ignore any errors that
// may occur here. If the status property is present, check whether
// it is set to 'ok' or 'okay', anything else is treated as 'disabled'.
//
NodeStatus = fdt_getprop (mDeviceTreeBase, Node, "status", &Len);
if (NodeStatus == NULL) {
return TRUE;
}
if ((Len >= 5) && (AsciiStrCmp (NodeStatus, "okay") == 0)) {
return TRUE;
}
if ((Len >= 3) && (AsciiStrCmp (NodeStatus, "ok") == 0)) {
return TRUE;
}
return FALSE;
}
STATIC
EFI_STATUS
EFIAPI
FindNextCompatibleNode (
IN FDT_CLIENT_PROTOCOL *This,
IN CONST CHAR8 *CompatibleString,
IN INT32 PrevNode,
OUT INT32 *Node
)
{
INT32 Prev, Next;
CONST CHAR8 *Type, *Compatible;
INT32 Len;
ASSERT (mDeviceTreeBase != NULL);
ASSERT (Node != NULL);
for (Prev = PrevNode; ; Prev = Next) {
Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
if (Next < 0) {
break;
}
if (!IsNodeEnabled (Next)) {
continue;
}
Type = fdt_getprop (mDeviceTreeBase, Next, "compatible", &Len);
if (Type == NULL) {
continue;
}
//
// A 'compatible' node may contain a sequence of NUL terminated
// compatible strings so check each one
//
for (Compatible = Type; Compatible < Type + Len && *Compatible;
Compatible += 1 + AsciiStrLen (Compatible))
{
if (AsciiStrCmp (CompatibleString, Compatible) == 0) {
*Node = Next;
return EFI_SUCCESS;
}
}
}
return EFI_NOT_FOUND;
}
STATIC
EFI_STATUS
EFIAPI
FindCompatibleNode (
IN FDT_CLIENT_PROTOCOL *This,
IN CONST CHAR8 *CompatibleString,
OUT INT32 *Node
)
{
return FindNextCompatibleNode (This, CompatibleString, 0, Node);
}
STATIC
EFI_STATUS
EFIAPI
FindCompatibleNodeProperty (
IN FDT_CLIENT_PROTOCOL *This,
IN CONST CHAR8 *CompatibleString,
IN CONST CHAR8 *PropertyName,
OUT CONST VOID **Prop,
OUT UINT32 *PropSize OPTIONAL
)
{
EFI_STATUS Status;
INT32 Node;
Status = FindCompatibleNode (This, CompatibleString, &Node);
if (EFI_ERROR (Status)) {
return Status;
}
return GetNodeProperty (This, Node, PropertyName, Prop, PropSize);
}
STATIC
EFI_STATUS
EFIAPI
FindCompatibleNodeReg (
IN FDT_CLIENT_PROTOCOL *This,
IN CONST CHAR8 *CompatibleString,
OUT CONST VOID **Reg,
OUT UINTN *AddressCells,
OUT UINTN *SizeCells,
OUT UINT32 *RegSize
)
{
EFI_STATUS Status;
ASSERT (RegSize != NULL);
//
// Get the 'reg' property of this node. For now, we will assume
// 8 byte quantities for base and size, respectively.
// TODO use #cells root properties instead
//
Status = FindCompatibleNodeProperty (
This,
CompatibleString,
"reg",
Reg,
RegSize
);
if (EFI_ERROR (Status)) {
return Status;
}
if ((*RegSize % 16) != 0) {
DEBUG ((
DEBUG_ERROR,
"%a: '%a' compatible node has invalid 'reg' property (size == 0x%x)\n",
__func__,
CompatibleString,
*RegSize
));
return EFI_NOT_FOUND;
}
*AddressCells = 2;
*SizeCells = 2;
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
EFIAPI
FindNextMemoryNodeReg (
IN FDT_CLIENT_PROTOCOL *This,
IN INT32 PrevNode,
OUT INT32 *Node,
OUT CONST VOID **Reg,
OUT UINTN *AddressCells,
OUT UINTN *SizeCells,
OUT UINT32 *RegSize
)
{
INT32 Prev, Next;
CONST CHAR8 *DeviceType;
INT32 Len;
EFI_STATUS Status;
ASSERT (mDeviceTreeBase != NULL);
ASSERT (Node != NULL);
for (Prev = PrevNode; ; Prev = Next) {
Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
if (Next < 0) {
break;
}
if (!IsNodeEnabled (Next)) {
DEBUG ((DEBUG_WARN, "%a: ignoring disabled memory node\n", __func__));
continue;
}
DeviceType = fdt_getprop (mDeviceTreeBase, Next, "device_type", &Len);
if ((DeviceType != NULL) && (AsciiStrCmp (DeviceType, "memory") == 0)) {
//
// Get the 'reg' property of this memory node. For now, we will assume
// 8 byte quantities for base and size, respectively.
// TODO use #cells root properties instead
//
Status = GetNodeProperty (This, Next, "reg", Reg, RegSize);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_WARN,
"%a: ignoring memory node with no 'reg' property\n",
__func__
));
continue;
}
if ((*RegSize % 16) != 0) {
DEBUG ((
DEBUG_WARN,
"%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",
__func__,
*RegSize
));
continue;
}
*Node = Next;
*AddressCells = 2;
*SizeCells = 2;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
STATIC
EFI_STATUS
EFIAPI
FindMemoryNodeReg (
IN FDT_CLIENT_PROTOCOL *This,
OUT INT32 *Node,
OUT CONST VOID **Reg,
OUT UINTN *AddressCells,
OUT UINTN *SizeCells,
OUT UINT32 *RegSize
)
{
return FindNextMemoryNodeReg (
This,
0,
Node,
Reg,
AddressCells,
SizeCells,
RegSize
);
}
STATIC
EFI_STATUS
EFIAPI
GetOrInsertChosenNode (
IN FDT_CLIENT_PROTOCOL *This,
OUT INT32 *Node
)
{
INT32 NewNode;
ASSERT (mDeviceTreeBase != NULL);
ASSERT (Node != NULL);
NewNode = fdt_path_offset (mDeviceTreeBase, "/chosen");
if (NewNode < 0) {
NewNode = fdt_add_subnode (mDeviceTreeBase, 0, "/chosen");
}
if (NewNode < 0) {
return EFI_OUT_OF_RESOURCES;
}
*Node = NewNode;
return EFI_SUCCESS;
}
STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol = {
GetNodeProperty,
SetNodeProperty,
FindCompatibleNode,
FindNextCompatibleNode,
FindCompatibleNodeProperty,
FindCompatibleNodeReg,
FindMemoryNodeReg,
FindNextMemoryNodeReg,
GetOrInsertChosenNode,
};
STATIC
VOID
EFIAPI
OnPlatformHasDeviceTree (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
VOID *Interface;
VOID *DeviceTreeBase;
Status = gBS->LocateProtocol (
&gEdkiiPlatformHasDeviceTreeGuid,
NULL, // Registration
&Interface
);
if (EFI_ERROR (Status)) {
return;
}
DeviceTreeBase = Context;
DEBUG ((
DEBUG_INFO,
"%a: exposing DTB @ 0x%p to OS\n",
__func__,
DeviceTreeBase
));
Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);
ASSERT_EFI_ERROR (Status);
gBS->CloseEvent (Event);
}
EFI_STATUS
EFIAPI
InitializeFdtClientDxe (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
VOID *Hob;
VOID *DeviceTreeBase;
EFI_STATUS Status;
EFI_EVENT PlatformHasDeviceTreeEvent;
VOID *Registration;
Hob = GetFirstGuidHob (&gFdtHobGuid);
if ((Hob == NULL) || (GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64))) {
return EFI_NOT_FOUND;
}
DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);
if (fdt_check_header (DeviceTreeBase) != 0) {
DEBUG ((
DEBUG_ERROR,
"%a: No DTB found @ 0x%p\n",
__func__,
DeviceTreeBase
));
return EFI_NOT_FOUND;
}
mDeviceTreeBase = DeviceTreeBase;
DEBUG ((DEBUG_INFO, "%a: DTB @ 0x%p\n", __func__, mDeviceTreeBase));
//
// Register a protocol notify for the EDKII Platform Has Device Tree
// Protocol.
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
OnPlatformHasDeviceTree,
DeviceTreeBase, // Context
&PlatformHasDeviceTreeEvent
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: CreateEvent(): %r\n", __func__, Status));
return Status;
}
Status = gBS->RegisterProtocolNotify (
&gEdkiiPlatformHasDeviceTreeGuid,
PlatformHasDeviceTreeEvent,
&Registration
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a: RegisterProtocolNotify(): %r\n",
__func__,
Status
));
goto CloseEvent;
}
//
// Kick the event; the protocol could be available already.
//
Status = gBS->SignalEvent (PlatformHasDeviceTreeEvent);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: SignalEvent(): %r\n", __func__, Status));
goto CloseEvent;
}
Status = gBS->InstallProtocolInterface (
&ImageHandle,
&gFdtClientProtocolGuid,
EFI_NATIVE_INTERFACE,
&mFdtClientProtocol
);
if (EFI_ERROR (Status)) {
DEBUG ((
DEBUG_ERROR,
"%a: InstallProtocolInterface(): %r\n",
__func__,
Status
));
goto CloseEvent;
}
return Status;
CloseEvent:
gBS->CloseEvent (PlatformHasDeviceTreeEvent);
return Status;
}