• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 *  FDT client driver
3 *
4 *  Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
5 *
6 *  This program and the accompanying materials are
7 *  licensed and made available under the terms and conditions of the BSD License
8 *  which accompanies this distribution.  The full text of the license may be found at
9 *  http://opensource.org/licenses/bsd-license.php
10 *
11 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 *
14 **/
15 
16 #include <Library/BaseLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/UefiDriverEntryPoint.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/HobLib.h>
21 #include <libfdt.h>
22 
23 #include <Guid/Fdt.h>
24 #include <Guid/FdtHob.h>
25 
26 #include <Protocol/FdtClient.h>
27 
28 STATIC VOID  *mDeviceTreeBase;
29 
30 STATIC
31 EFI_STATUS
GetNodeProperty(IN FDT_CLIENT_PROTOCOL * This,IN INT32 Node,IN CONST CHAR8 * PropertyName,OUT CONST VOID ** Prop,OUT UINT32 * PropSize OPTIONAL)32 GetNodeProperty (
33   IN  FDT_CLIENT_PROTOCOL     *This,
34   IN  INT32                   Node,
35   IN  CONST CHAR8             *PropertyName,
36   OUT CONST VOID              **Prop,
37   OUT UINT32                  *PropSize OPTIONAL
38   )
39 {
40   INT32 Len;
41 
42   ASSERT (mDeviceTreeBase != NULL);
43   ASSERT (Prop != NULL);
44 
45   *Prop = fdt_getprop (mDeviceTreeBase, Node, PropertyName, &Len);
46   if (*Prop == NULL) {
47     return EFI_NOT_FOUND;
48   }
49 
50   if (PropSize != NULL) {
51     *PropSize = Len;
52   }
53   return EFI_SUCCESS;
54 }
55 
56 STATIC
57 EFI_STATUS
SetNodeProperty(IN FDT_CLIENT_PROTOCOL * This,IN INT32 Node,IN CONST CHAR8 * PropertyName,IN CONST VOID * Prop,IN UINT32 PropSize)58 SetNodeProperty (
59   IN  FDT_CLIENT_PROTOCOL     *This,
60   IN  INT32                   Node,
61   IN  CONST CHAR8             *PropertyName,
62   IN  CONST VOID              *Prop,
63   IN  UINT32                  PropSize
64   )
65 {
66   INT32 Ret;
67 
68   ASSERT (mDeviceTreeBase != NULL);
69 
70   Ret = fdt_setprop (mDeviceTreeBase, Node, PropertyName, Prop, PropSize);
71   if (Ret != 0) {
72     return EFI_DEVICE_ERROR;
73   }
74 
75   return EFI_SUCCESS;
76 }
77 
78 STATIC
79 EFI_STATUS
80 EFIAPI
FindNextCompatibleNode(IN FDT_CLIENT_PROTOCOL * This,IN CONST CHAR8 * CompatibleString,IN INT32 PrevNode,OUT INT32 * Node)81 FindNextCompatibleNode (
82   IN  FDT_CLIENT_PROTOCOL     *This,
83   IN  CONST CHAR8             *CompatibleString,
84   IN  INT32                   PrevNode,
85   OUT INT32                   *Node
86   )
87 {
88   INT32          Prev, Next;
89   CONST CHAR8    *Type, *Compatible;
90   INT32          Len;
91 
92   ASSERT (mDeviceTreeBase != NULL);
93   ASSERT (Node != NULL);
94 
95   for (Prev = PrevNode;; Prev = Next) {
96     Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
97     if (Next < 0) {
98       break;
99     }
100 
101     Type = fdt_getprop (mDeviceTreeBase, Next, "compatible", &Len);
102     if (Type == NULL) {
103       continue;
104     }
105 
106     //
107     // A 'compatible' node may contain a sequence of NUL terminated
108     // compatible strings so check each one
109     //
110     for (Compatible = Type; Compatible < Type + Len && *Compatible;
111          Compatible += 1 + AsciiStrLen (Compatible)) {
112       if (AsciiStrCmp (CompatibleString, Compatible) == 0) {
113         *Node = Next;
114         return EFI_SUCCESS;
115       }
116     }
117   }
118   return EFI_NOT_FOUND;
119 }
120 
121 STATIC
122 EFI_STATUS
123 EFIAPI
FindCompatibleNode(IN FDT_CLIENT_PROTOCOL * This,IN CONST CHAR8 * CompatibleString,OUT INT32 * Node)124 FindCompatibleNode (
125   IN  FDT_CLIENT_PROTOCOL     *This,
126   IN  CONST CHAR8             *CompatibleString,
127   OUT INT32                   *Node
128   )
129 {
130   return FindNextCompatibleNode (This, CompatibleString, 0, Node);
131 }
132 
133 STATIC
134 EFI_STATUS
135 EFIAPI
FindCompatibleNodeProperty(IN FDT_CLIENT_PROTOCOL * This,IN CONST CHAR8 * CompatibleString,IN CONST CHAR8 * PropertyName,OUT CONST VOID ** Prop,OUT UINT32 * PropSize OPTIONAL)136 FindCompatibleNodeProperty (
137   IN  FDT_CLIENT_PROTOCOL     *This,
138   IN  CONST CHAR8             *CompatibleString,
139   IN  CONST CHAR8             *PropertyName,
140   OUT CONST VOID              **Prop,
141   OUT UINT32                  *PropSize OPTIONAL
142   )
143 {
144   EFI_STATUS        Status;
145   INT32             Node;
146 
147   Status = FindCompatibleNode (This, CompatibleString, &Node);
148   if (EFI_ERROR (Status)) {
149     return Status;
150   }
151 
152   return GetNodeProperty (This, Node, PropertyName, Prop, PropSize);
153 }
154 
155 STATIC
156 EFI_STATUS
157 EFIAPI
FindCompatibleNodeReg(IN FDT_CLIENT_PROTOCOL * This,IN CONST CHAR8 * CompatibleString,OUT CONST VOID ** Reg,OUT UINTN * AddressCells,OUT UINTN * SizeCells,OUT UINT32 * RegSize)158 FindCompatibleNodeReg (
159   IN  FDT_CLIENT_PROTOCOL     *This,
160   IN  CONST CHAR8             *CompatibleString,
161   OUT CONST VOID              **Reg,
162   OUT UINTN                   *AddressCells,
163   OUT UINTN                   *SizeCells,
164   OUT UINT32                  *RegSize
165   )
166 {
167   EFI_STATUS Status;
168 
169   ASSERT (RegSize != NULL);
170 
171   //
172   // Get the 'reg' property of this node. For now, we will assume
173   // 8 byte quantities for base and size, respectively.
174   // TODO use #cells root properties instead
175   //
176   Status = FindCompatibleNodeProperty (This, CompatibleString, "reg", Reg,
177              RegSize);
178   if (EFI_ERROR (Status)) {
179     return Status;
180   }
181 
182   if ((*RegSize % 16) != 0) {
183     DEBUG ((EFI_D_ERROR,
184       "%a: '%a' compatible node has invalid 'reg' property (size == 0x%x)\n",
185       __FUNCTION__, CompatibleString, *RegSize));
186     return EFI_NOT_FOUND;
187   }
188 
189   *AddressCells = 2;
190   *SizeCells = 2;
191 
192   return EFI_SUCCESS;
193 }
194 
195 STATIC
196 EFI_STATUS
197 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)198 FindNextMemoryNodeReg (
199   IN  FDT_CLIENT_PROTOCOL     *This,
200   IN  INT32                   PrevNode,
201   OUT INT32                   *Node,
202   OUT CONST VOID              **Reg,
203   OUT UINTN                   *AddressCells,
204   OUT UINTN                   *SizeCells,
205   OUT UINT32                  *RegSize
206   )
207 {
208   INT32          Prev, Next;
209   CONST CHAR8    *DeviceType;
210   INT32          Len;
211   EFI_STATUS     Status;
212 
213   ASSERT (mDeviceTreeBase != NULL);
214   ASSERT (Node != NULL);
215 
216   for (Prev = PrevNode;; Prev = Next) {
217     Next = fdt_next_node (mDeviceTreeBase, Prev, NULL);
218     if (Next < 0) {
219       break;
220     }
221 
222     DeviceType = fdt_getprop (mDeviceTreeBase, Next, "device_type", &Len);
223     if (DeviceType != NULL && AsciiStrCmp (DeviceType, "memory") == 0) {
224       //
225       // Get the 'reg' property of this memory node. For now, we will assume
226       // 8 byte quantities for base and size, respectively.
227       // TODO use #cells root properties instead
228       //
229       Status = GetNodeProperty (This, Next, "reg", Reg, RegSize);
230       if (EFI_ERROR (Status)) {
231         DEBUG ((EFI_D_WARN,
232           "%a: ignoring memory node with no 'reg' property\n",
233           __FUNCTION__));
234         continue;
235       }
236       if ((*RegSize % 16) != 0) {
237         DEBUG ((EFI_D_WARN,
238           "%a: ignoring memory node with invalid 'reg' property (size == 0x%x)\n",
239           __FUNCTION__, *RegSize));
240         continue;
241       }
242 
243       *Node = Next;
244       *AddressCells = 2;
245       *SizeCells = 2;
246       return EFI_SUCCESS;
247     }
248   }
249   return EFI_NOT_FOUND;
250 }
251 
252 STATIC
253 EFI_STATUS
254 EFIAPI
FindMemoryNodeReg(IN FDT_CLIENT_PROTOCOL * This,OUT INT32 * Node,OUT CONST VOID ** Reg,OUT UINTN * AddressCells,OUT UINTN * SizeCells,OUT UINT32 * RegSize)255 FindMemoryNodeReg (
256   IN  FDT_CLIENT_PROTOCOL     *This,
257   OUT INT32                   *Node,
258   OUT CONST VOID              **Reg,
259   OUT UINTN                   *AddressCells,
260   OUT UINTN                   *SizeCells,
261   OUT UINT32                  *RegSize
262   )
263 {
264   return FindNextMemoryNodeReg (This, 0, Node, Reg, AddressCells, SizeCells,
265            RegSize);
266 }
267 
268 STATIC
269 EFI_STATUS
GetOrInsertChosenNode(IN FDT_CLIENT_PROTOCOL * This,OUT INT32 * Node)270 GetOrInsertChosenNode (
271   IN  FDT_CLIENT_PROTOCOL     *This,
272   OUT INT32                   *Node
273   )
274 {
275   INT32 NewNode;
276 
277   ASSERT (mDeviceTreeBase != NULL);
278   ASSERT (Node != NULL);
279 
280   NewNode = fdt_path_offset (mDeviceTreeBase, "/chosen");
281   if (NewNode < 0) {
282     NewNode = fdt_add_subnode (mDeviceTreeBase, 0, "/chosen");
283   }
284 
285   if (NewNode < 0) {
286     return EFI_OUT_OF_RESOURCES;
287   }
288 
289   *Node = NewNode;
290 
291   return EFI_SUCCESS;
292 }
293 
294 STATIC FDT_CLIENT_PROTOCOL mFdtClientProtocol = {
295   GetNodeProperty,
296   SetNodeProperty,
297   FindCompatibleNode,
298   FindNextCompatibleNode,
299   FindCompatibleNodeProperty,
300   FindCompatibleNodeReg,
301   FindMemoryNodeReg,
302   FindNextMemoryNodeReg,
303   GetOrInsertChosenNode,
304 };
305 
306 EFI_STATUS
307 EFIAPI
InitializeFdtClientDxe(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)308 InitializeFdtClientDxe (
309   IN EFI_HANDLE           ImageHandle,
310   IN EFI_SYSTEM_TABLE     *SystemTable
311   )
312 {
313   VOID              *Hob;
314   VOID              *DeviceTreeBase;
315   EFI_STATUS        Status;
316 
317   Hob = GetFirstGuidHob (&gFdtHobGuid);
318   if (Hob == NULL || GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)) {
319     return EFI_NOT_FOUND;
320   }
321   DeviceTreeBase = (VOID *)(UINTN)*(UINT64 *)GET_GUID_HOB_DATA (Hob);
322 
323   if (fdt_check_header (DeviceTreeBase) != 0) {
324     DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__,
325       DeviceTreeBase));
326     return EFI_NOT_FOUND;
327   }
328 
329   mDeviceTreeBase = DeviceTreeBase;
330 
331   DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, mDeviceTreeBase));
332 
333   if (!FeaturePcdGet (PcdPureAcpiBoot)) {
334     //
335     // Only install the FDT as a configuration table if we want to leave it up
336     // to the OS to decide whether it prefers ACPI over DT.
337     //
338     Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);
339     ASSERT_EFI_ERROR (Status);
340   }
341 
342   return gBS->InstallProtocolInterface (&ImageHandle, &gFdtClientProtocolGuid,
343                 EFI_NATIVE_INTERFACE, &mFdtClientProtocol);
344 }
345