]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Library/BdsLib/BdsLinuxFdt.c
ArmPlatformPkg: Moved ARMv7 specific files to a 'Arm' subdirectory
[mirror_edk2.git] / ArmPkg / Library / BdsLib / BdsLinuxFdt.c
CommitLineData
0a6653bc 1/** @file\r
2*\r
3* Copyright (c) 2011-2012, ARM Limited. All rights reserved.\r
4* \r
5* This program and the accompanying materials \r
6* are licensed and made available under the terms and conditions of the BSD License \r
7* which accompanies this distribution. The full text of the license may be found at \r
8* http://opensource.org/licenses/bsd-license.php \r
9*\r
10* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
11* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
12*\r
13**/\r
14\r
15#include <Library/PcdLib.h>\r
16#include <libfdt.h>\r
17\r
18#include "BdsInternal.h"\r
19#include "BdsLinuxLoader.h"\r
20\r
21#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))\r
22#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))\r
23#define GET_CELL(p) (p += 4, *((const UINT32 *)(p-4)))\r
24\r
25STATIC\r
26UINTN\r
27IsPrintableString (\r
28 IN CONST VOID* data,\r
29 IN UINTN len\r
30 )\r
31{\r
32 CONST CHAR8 *s = data;\r
33 CONST CHAR8 *ss;\r
34\r
35 // Zero length is not\r
36 if (len == 0) {\r
37 return 0;\r
38 }\r
39\r
40 // Must terminate with zero\r
41 if (s[len - 1] != '\0') {\r
42 return 0;\r
43 }\r
44\r
45 ss = s;\r
46 while (*s/* && isprint(*s)*/) {\r
47 s++;\r
48 }\r
49\r
50 // Not zero, or not done yet\r
51 if (*s != '\0' || (s + 1 - ss) < len) {\r
52 return 0;\r
53 }\r
54\r
55 return 1;\r
56}\r
57\r
58STATIC\r
59VOID\r
60PrintData (\r
61 IN CONST CHAR8* data,\r
62 IN UINTN len\r
63 )\r
64{\r
65 UINTN i;\r
66 CONST CHAR8 *p = data;\r
67\r
68 // No data, don't print\r
69 if (len == 0)\r
70 return;\r
71\r
72 if (IsPrintableString (data, len)) {\r
73 Print(L" = \"%a\"", (const char *)data);\r
74 } else if ((len % 4) == 0) {\r
75 Print(L" = <");\r
76 for (i = 0; i < len; i += 4) {\r
77 Print(L"0x%08x%a", fdt32_to_cpu(GET_CELL(p)),i < (len - 4) ? " " : "");\r
78 }\r
79 Print(L">");\r
80 } else {\r
81 Print(L" = [");\r
82 for (i = 0; i < len; i++)\r
83 Print(L"%02x%a", *p++, i < len - 1 ? " " : "");\r
84 Print(L"]");\r
85 }\r
86}\r
87\r
88VOID\r
89DebugDumpFdt (\r
90 IN VOID* FdtBlob\r
91 )\r
92{\r
93 struct fdt_header *bph;\r
94 UINT32 off_dt;\r
95 UINT32 off_str;\r
96 CONST CHAR8* p_struct;\r
97 CONST CHAR8* p_strings;\r
98 CONST CHAR8* p;\r
99 CONST CHAR8* s;\r
100 CONST CHAR8* t;\r
101 UINT32 tag;\r
102 UINTN sz;\r
103 UINTN depth;\r
104 UINTN shift;\r
105 UINT32 version;\r
106\r
107 depth = 0;\r
108 shift = 4;\r
109\r
110 bph = FdtBlob;\r
111 off_dt = fdt32_to_cpu(bph->off_dt_struct);\r
112 off_str = fdt32_to_cpu(bph->off_dt_strings);\r
113 p_struct = (CONST CHAR8*)FdtBlob + off_dt;\r
114 p_strings = (CONST CHAR8*)FdtBlob + off_str;\r
115 version = fdt32_to_cpu(bph->version);\r
116\r
117 p = p_struct;\r
118 while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) {\r
119 if (tag == FDT_BEGIN_NODE) {\r
120 s = p;\r
121 p = PALIGN(p + AsciiStrLen (s) + 1, 4);\r
122\r
123 if (*s == '\0')\r
124 s = "/";\r
125\r
126 Print(L"%*s%a {\n", depth * shift, L" ", s);\r
127\r
128 depth++;\r
129 continue;\r
130 }\r
131\r
132 if (tag == FDT_END_NODE) {\r
133 depth--;\r
134\r
135 Print(L"%*s};\n", depth * shift, L" ");\r
136 continue;\r
137 }\r
138\r
139 if (tag == FDT_NOP) {\r
140 Print(L"%*s// [NOP]\n", depth * shift, L" ");\r
141 continue;\r
142 }\r
143\r
144 if (tag != FDT_PROP) {\r
145 Print(L"%*s ** Unknown tag 0x%08x\n", depth * shift, L" ", tag);\r
146 break;\r
147 }\r
148 sz = fdt32_to_cpu(GET_CELL(p));\r
149 s = p_strings + fdt32_to_cpu(GET_CELL(p));\r
150 if (version < 16 && sz >= 8)\r
151 p = PALIGN(p, 8);\r
152 t = p;\r
153\r
154 p = PALIGN(p + sz, 4);\r
155\r
156 Print(L"%*s%a", depth * shift, L" ", s);\r
157 PrintData(t, sz);\r
158 Print(L";\n");\r
159 }\r
160}\r
161\r
162typedef struct {\r
163 UINTN Base;\r
164 UINTN Size;\r
165} FdtRegion;\r
166\r
167EFI_STATUS\r
168PrepareFdt (\r
169 IN CONST CHAR8* CommandLineArguments,\r
170 IN EFI_PHYSICAL_ADDRESS InitrdImage,\r
171 IN UINTN InitrdImageSize,\r
172 IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase,\r
c63626b7 173 IN OUT UINTN *FdtBlobSize\r
0a6653bc 174 )\r
175{\r
176 EFI_STATUS Status;\r
177 EFI_PHYSICAL_ADDRESS NewFdtBlobBase;\r
178 UINTN NewFdtBlobSize;\r
179 VOID* fdt;\r
180 INTN err;\r
181 INTN node;\r
182 INTN cpu_node;\r
183 INTN lenp;\r
184 CONST VOID* BootArg;\r
185 EFI_PHYSICAL_ADDRESS InitrdImageStart;\r
186 EFI_PHYSICAL_ADDRESS InitrdImageEnd;\r
187 FdtRegion Region;\r
188 UINTN Index;\r
189 CHAR8 Name[10];\r
190 LIST_ENTRY ResourceList;\r
191 BDS_SYSTEM_MEMORY_RESOURCE *Resource;\r
192 ARM_PROCESSOR_TABLE *ArmProcessorTable;\r
193 ARM_CORE_INFO *ArmCoreInfoTable;\r
194 UINT32 MpId;\r
195 UINT32 ClusterId;\r
196 UINT32 CoreId;\r
197 UINT64 CpuReleaseAddr;\r
198\r
199 err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase));\r
200 if (err != 0) {\r
201 Print (L"ERROR: Device Tree header not valid (err:%d)\n", err);\r
202 return EFI_INVALID_PARAMETER;\r
203 }\r
204\r
205 //\r
206 // Allocate memory for the new FDT\r
207 //\r
208 NewFdtBlobSize = fdt_totalsize((VOID*)(UINTN)(*FdtBlobBase)) + FDT_ADDITIONAL_ENTRIES_SIZE;\r
209\r
210 // Try below a watermark address\r
211 Status = EFI_NOT_FOUND;\r
212 if (PcdGet32(PcdArmLinuxFdtMaxOffset) != 0) {\r
213 NewFdtBlobBase = LINUX_FDT_MAX_OFFSET;\r
214 Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, EFI_SIZE_TO_PAGES(NewFdtBlobSize), &NewFdtBlobBase);\r
215 if (EFI_ERROR(Status)) {\r
216 DEBUG ((EFI_D_WARN, "Warning: Failed to load FDT below address 0x%lX (%r). Will try again at a random address anywhere.\n", NewFdtBlobBase, Status));\r
217 }\r
218 }\r
219\r
220 // Try anywhere there is available space\r
221 if (EFI_ERROR(Status)) {\r
222 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES(NewFdtBlobSize), &NewFdtBlobBase);\r
223 if (EFI_ERROR(Status)) {\r
224 ASSERT_EFI_ERROR(Status);\r
225 goto FAIL_NEW_FDT;\r
226 } else {\r
227 DEBUG ((EFI_D_WARN, "WARNING: Loaded FDT at random address 0x%lX.\nWARNING: There is a risk of accidental overwriting by other code/data.\n", NewFdtBlobBase));\r
228 }\r
229 }\r
230\r
231 // Load the Original FDT tree into the new region\r
232 fdt = (VOID*)(UINTN)NewFdtBlobBase;\r
233 err = fdt_open_into((VOID*)(UINTN)(*FdtBlobBase), fdt, NewFdtBlobSize);\r
234 if (err) {\r
235 DEBUG((EFI_D_ERROR, "fdt_open_into(): %a\n", fdt_strerror(err)));\r
236 Status = EFI_INVALID_PARAMETER;\r
237 goto FAIL_NEW_FDT;\r
238 }\r
239\r
240 DEBUG_CODE_BEGIN();\r
241 //DebugDumpFdt (fdt);\r
242 DEBUG_CODE_END();\r
243\r
244 node = fdt_subnode_offset(fdt, 0, "chosen");\r
245 if (node < 0) {\r
246 // The 'chosen' node does not exist, create it\r
247 node = fdt_add_subnode(fdt, 0, "chosen");\r
248 if (node < 0) {\r
249 DEBUG((EFI_D_ERROR,"Error on finding 'chosen' node\n"));\r
250 Status = EFI_INVALID_PARAMETER;\r
251 goto FAIL_NEW_FDT;\r
252 }\r
253 }\r
254\r
255 DEBUG_CODE_BEGIN();\r
256 BootArg = fdt_getprop(fdt, node, "bootargs", &lenp);\r
257 if (BootArg != NULL) {\r
258 DEBUG((EFI_D_ERROR,"BootArg: %a\n",BootArg));\r
259 }\r
260 DEBUG_CODE_END();\r
261\r
262 // Set Linux CmdLine\r
263 if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) {\r
264 err = fdt_setprop(fdt, node, "bootargs", CommandLineArguments, AsciiStrSize(CommandLineArguments));\r
265 if (err) {\r
266 DEBUG((EFI_D_ERROR,"Fail to set new 'bootarg' (err:%d)\n",err));\r
267 }\r
268 }\r
269\r
270 // Set Linux Initrd\r
271 if (InitrdImageSize != 0) {\r
272 InitrdImageStart = cpu_to_fdt64 (InitrdImage);\r
273 err = fdt_setprop(fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof(EFI_PHYSICAL_ADDRESS));\r
274 if (err) {\r
275 DEBUG((EFI_D_ERROR,"Fail to set new 'linux,initrd-start' (err:%d)\n",err));\r
276 }\r
277 InitrdImageEnd = cpu_to_fdt64 (InitrdImage + InitrdImageSize);\r
278 err = fdt_setprop(fdt, node, "linux,initrd-end", &InitrdImageEnd, sizeof(EFI_PHYSICAL_ADDRESS));\r
279 if (err) {\r
280 DEBUG((EFI_D_ERROR,"Fail to set new 'linux,initrd-start' (err:%d)\n",err));\r
281 }\r
282 }\r
283\r
284 // Set Physical memory setup if does not exist\r
285 node = fdt_subnode_offset(fdt, 0, "memory");\r
286 if (node < 0) {\r
287 // The 'memory' node does not exist, create it\r
288 node = fdt_add_subnode(fdt, 0, "memory");\r
289 if (node >= 0) {\r
290 fdt_setprop_string(fdt, node, "name", "memory");\r
291 fdt_setprop_string(fdt, node, "device_type", "memory");\r
292 \r
293 GetSystemMemoryResources (&ResourceList);\r
294 Resource = (BDS_SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink;\r
295 \r
296 if (sizeof(UINTN) == sizeof(UINT32)) {\r
297 Region.Base = cpu_to_fdt32((UINTN)Resource->PhysicalStart);\r
298 Region.Size = cpu_to_fdt32((UINTN)Resource->ResourceLength);\r
299 } else {\r
300 Region.Base = cpu_to_fdt64((UINTN)Resource->PhysicalStart);\r
301 Region.Size = cpu_to_fdt64((UINTN)Resource->ResourceLength);\r
302 }\r
303\r
304 err = fdt_setprop(fdt, node, "reg", &Region, sizeof(Region));\r
305 if (err) {\r
306 DEBUG((EFI_D_ERROR,"Fail to set new 'memory region' (err:%d)\n",err));\r
307 }\r
308 }\r
309 }\r
310\r
311 // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms\r
312 for (Index=0; Index < gST->NumberOfTableEntries; Index++) {\r
313 // Check for correct GUID type\r
314 if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {\r
315 MpId = ArmReadMpidr ();\r
316 ClusterId = GET_CLUSTER_ID(MpId);\r
317 CoreId = GET_CORE_ID(MpId);\r
318\r
319 node = fdt_subnode_offset(fdt, 0, "cpus");\r
320 if (node < 0) {\r
321 // Create the /cpus node\r
322 node = fdt_add_subnode(fdt, 0, "cpus");\r
323 fdt_setprop_string(fdt, node, "name", "cpus");\r
324 fdt_setprop_cell(fdt, node, "#address-cells", 1);\r
325 fdt_setprop_cell(fdt, node, "#size-cells", 0);\r
326 }\r
327\r
328 // Get pointer to ARM processor table\r
329 ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable;\r
330 ArmCoreInfoTable = ArmProcessorTable->ArmCpus;\r
331\r
332 for (Index = 0; Index < ArmProcessorTable->NumberOfEntries; Index++) {\r
333 AsciiSPrint (Name, 10, "cpu@%d", Index);\r
334 cpu_node = fdt_subnode_offset(fdt, node, Name);\r
335 if (cpu_node < 0) {\r
336 cpu_node = fdt_add_subnode(fdt, node, Name);\r
337 fdt_setprop_string(fdt, cpu_node, "device-type", "cpu");\r
338 fdt_setprop(fdt, cpu_node, "reg", &Index, sizeof(Index));\r
339 }\r
340\r
341 fdt_setprop_string(fdt, cpu_node, "enable-method", "spin-table");\r
342 CpuReleaseAddr = cpu_to_fdt64(ArmCoreInfoTable[Index].MailboxSetAddress);\r
343 fdt_setprop(fdt, cpu_node, "cpu-release-addr", &CpuReleaseAddr, sizeof(CpuReleaseAddr));\r
344\r
345 // If it is not the primary core than the cpu should be disabled\r
346 if (((ArmCoreInfoTable[Index].ClusterId != ClusterId) || (ArmCoreInfoTable[Index].CoreId != CoreId))) {\r
347 fdt_setprop_string(fdt, cpu_node, "status", "disabled");\r
348 }\r
349 }\r
350 break;\r
351 }\r
352 }\r
353\r
354 DEBUG_CODE_BEGIN();\r
355 //DebugDumpFdt (fdt);\r
356 DEBUG_CODE_END();\r
357\r
358 *FdtBlobBase = NewFdtBlobBase;\r
359 *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase));\r
360 return EFI_SUCCESS;\r
361\r
362FAIL_NEW_FDT:\r
363 *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));\r
364 // Return success even if we failed to update the FDT blob. The original one is still valid.\r
365 return EFI_SUCCESS;\r
366}\r
367\r
368\r