1 /** @file
2 *
3 * Copyright (c) 2013-2015, ARM Limited. All rights reserved.
4 *
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
9 *
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14
15 #include "ArmJunoDxeInternal.h"
16 #include <ArmPlatform.h>
17
18 #include <Protocol/DevicePathFromText.h>
19 #include <Protocol/PciRootBridgeIo.h>
20
21 #include <Guid/EventGroup.h>
22 #include <Guid/GlobalVariable.h>
23
24 #include <Library/ArmShellCmdLib.h>
25 #include <Library/AcpiLib.h>
26 #include <Library/BaseMemoryLib.h>
27 #include <Library/DevicePathLib.h>
28 #include <Library/MemoryAllocationLib.h>
29 #include <Library/UefiRuntimeServicesTableLib.h>
30 #include <Library/IoLib.h>
31 #include <Library/PrintLib.h>
32
33 //
34 // Size in number of characters of the Linux boot argument
35 // passing the MAC address to be used by the PCI GigaByte
36 // Ethernet device : " sky2.mac_address=0x11,0x22,0x33,0x44,0x55,0x66"
37 //
38 #define SKY2_MAC_ADDRESS_BOOTARG_LEN 47
39
40 //
41 // Hardware platform identifiers
42 //
43 typedef enum {
44 UNKNOWN,
45 JUNO_R0,
46 JUNO_R1
47 } JUNO_REVISION;
48
49 //
50 // Function prototypes
51 //
52 STATIC EFI_STATUS SetJunoR1DefaultBootEntries (
53 VOID
54 );
55
56 // This GUID must match the FILE_GUID in ArmPlatformPkg/ArmJunoPkg/AcpiTables/AcpiTables.inf
57 STATIC CONST EFI_GUID mJunoAcpiTableFile = { 0xa1dd808e, 0x1e95, 0x4399, { 0xab, 0xc0, 0x65, 0x3c, 0x82, 0xe8, 0x53, 0x0c } };
58
59 typedef struct {
60 ACPI_HID_DEVICE_PATH AcpiDevicePath;
61 PCI_DEVICE_PATH PciDevicePath;
62 EFI_DEVICE_PATH_PROTOCOL EndDevicePath;
63 } EFI_PCI_ROOT_BRIDGE_DEVICE_PATH;
64
65 STATIC CONST EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mPciRootComplexDevicePath = {
66 {
67 { ACPI_DEVICE_PATH,
68 ACPI_DP,
69 { (UINT8) (sizeof (ACPI_HID_DEVICE_PATH)),
70 (UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8) }
71 },
72 EISA_PNP_ID (0x0A03),
73 0
74 },
75 {
76 { HARDWARE_DEVICE_PATH,
77 HW_PCI_DP,
78 { (UINT8) (sizeof (PCI_DEVICE_PATH)),
79 (UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8) }
80 },
81 0,
82 0
83 },
84 {
85 END_DEVICE_PATH_TYPE,
86 END_ENTIRE_DEVICE_PATH_SUBTYPE,
87 { END_DEVICE_PATH_LENGTH, 0 }
88 }
89 };
90
91 EFI_EVENT mAcpiRegistration = NULL;
92
93 /**
94 * Build and Set UEFI Variable Boot####
95 *
96 * @param BootVariableName Name of the UEFI Variable
97 * @param Attributes 'Attributes' for the Boot#### variable as per UEFI spec
98 * @param BootDescription Description of the Boot#### variable
99 * @param DevicePath EFI Device Path of the EFI Application to boot
100 * @param OptionalData Parameters to pass to the EFI application
101 * @param OptionalDataSize Size of the parameters to pass to the EFI application
102 *
103 * @return EFI_OUT_OF_RESOURCES A memory allocation failed
104 * @return Return value of RT.SetVariable
105 */
106 STATIC
107 EFI_STATUS
BootOptionCreate(IN CHAR16 BootVariableName[9],IN UINT32 Attributes,IN CHAR16 * BootDescription,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN UINT8 * OptionalData,IN UINTN OptionalDataSize)108 BootOptionCreate (
109 IN CHAR16 BootVariableName[9],
110 IN UINT32 Attributes,
111 IN CHAR16* BootDescription,
112 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
113 IN UINT8* OptionalData,
114 IN UINTN OptionalDataSize
115 )
116 {
117 UINTN VariableSize;
118 UINT8 *Variable;
119 UINT8 *VariablePtr;
120 UINTN FilePathListLength;
121 UINTN BootDescriptionSize;
122
123 FilePathListLength = GetDevicePathSize (DevicePath);
124 BootDescriptionSize = StrSize (BootDescription);
125
126 // Each Boot#### variable is built as follow:
127 // UINT32 Attributes
128 // UINT16 FilePathListLength
129 // CHAR16* Description
130 // EFI_DEVICE_PATH_PROTOCOL FilePathList[]
131 // UINT8 OptionalData[]
132 VariableSize = sizeof (UINT32) + sizeof (UINT16) +
133 BootDescriptionSize + FilePathListLength + OptionalDataSize;
134 Variable = AllocateZeroPool (VariableSize);
135 if (Variable == NULL) {
136 return EFI_OUT_OF_RESOURCES;
137 }
138
139 // 'Attributes' field
140 *(UINT32*)Variable = Attributes;
141 // 'FilePathListLength' field
142 VariablePtr = Variable + sizeof (UINT32);
143 *(UINT16*)VariablePtr = FilePathListLength;
144 // 'Description' field
145 VariablePtr += sizeof (UINT16);
146 CopyMem (VariablePtr, BootDescription, BootDescriptionSize);
147 // 'FilePathList' field
148 VariablePtr += BootDescriptionSize;
149 CopyMem (VariablePtr, DevicePath, FilePathListLength);
150 // 'OptionalData' field
151 VariablePtr += FilePathListLength;
152 CopyMem (VariablePtr, OptionalData, OptionalDataSize);
153
154 return gRT->SetVariable (
155 BootVariableName,
156 &gEfiGlobalVariableGuid,
157 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
158 VariableSize, Variable
159 );
160 }
161
162 /**
163 Notification function of the event defined as belonging to the
164 EFI_END_OF_DXE_EVENT_GROUP_GUID event group that was created in
165 the entry point of the driver.
166
167 This function is called when an event belonging to the
168 EFI_END_OF_DXE_EVENT_GROUP_GUID event group is signalled. Such an
169 event is signalled once at the end of the dispatching of all
170 drivers (end of the so called DXE phase).
171
172 @param[in] Event Event declared in the entry point of the driver whose
173 notification function is being invoked.
174 @param[in] Context NULL
175 **/
176 STATIC
177 VOID
OnEndOfDxe(IN EFI_EVENT Event,IN VOID * Context)178 OnEndOfDxe (
179 IN EFI_EVENT Event,
180 IN VOID *Context
181 )
182 {
183 EFI_DEVICE_PATH_PROTOCOL* PciRootComplexDevicePath;
184 EFI_HANDLE Handle;
185 EFI_STATUS Status;
186
187 //
188 // PCI Root Complex initialization
189 // At the end of the DXE phase, we should get all the driver dispatched.
190 // Force the PCI Root Complex to be initialized. It allows the OS to skip
191 // this step.
192 //
193 PciRootComplexDevicePath = (EFI_DEVICE_PATH_PROTOCOL*) &mPciRootComplexDevicePath;
194 Status = gBS->LocateDevicePath (&gEfiPciRootBridgeIoProtocolGuid,
195 &PciRootComplexDevicePath,
196 &Handle);
197
198 Status = gBS->ConnectController (Handle, NULL, PciRootComplexDevicePath, FALSE);
199 ASSERT_EFI_ERROR (Status);
200 }
201
202 STATIC
203 BOOLEAN
AcpiTableJunoR0Check(IN EFI_ACPI_DESCRIPTION_HEADER * AcpiHeader)204 AcpiTableJunoR0Check (
205 IN EFI_ACPI_DESCRIPTION_HEADER *AcpiHeader
206 )
207 {
208 return TRUE;
209 }
210
211 STATIC
212 BOOLEAN
AcpiTableJunoR1Check(IN EFI_ACPI_DESCRIPTION_HEADER * AcpiHeader)213 AcpiTableJunoR1Check (
214 IN EFI_ACPI_DESCRIPTION_HEADER *AcpiHeader
215 )
216 {
217 return TRUE;
218 }
219
220 EFI_STATUS
221 EFIAPI
ArmJunoEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)222 ArmJunoEntryPoint (
223 IN EFI_HANDLE ImageHandle,
224 IN EFI_SYSTEM_TABLE *SystemTable
225 )
226 {
227 EFI_STATUS Status;
228 EFI_PHYSICAL_ADDRESS HypBase;
229 CHAR16 *TextDevicePath;
230 UINTN TextDevicePathSize;
231 VOID *Buffer;
232 UINT32 Midr;
233 UINT32 CpuType;
234 UINT32 CpuRev;
235 JUNO_REVISION JunoRevision;
236 EFI_EVENT EndOfDxeEvent;
237
238 JunoRevision = UNKNOWN;
239 Status = PciEmulationEntryPoint ();
240 if (EFI_ERROR (Status)) {
241 return Status;
242 }
243
244 //
245 // If a hypervisor has been declared then we need to make sure its region is protected at runtime
246 //
247 // Note: This code is only a workaround for our dummy hypervisor (ArmPkg/Extra/AArch64ToAArch32Shim/)
248 // that does not set up (yet) the stage 2 translation table to hide its own memory to EL1.
249 //
250 if (FixedPcdGet32 (PcdHypFvSize) != 0) {
251 // Ensure the hypervisor region is strictly contained into a EFI_PAGE_SIZE-aligned region.
252 // The memory must be a multiple of EFI_PAGE_SIZE to ensure we do not reserve more memory than the hypervisor itself.
253 // A UEFI Runtime region size granularity cannot be smaller than EFI_PAGE_SIZE. If the hypervisor size is not rounded
254 // to this size then there is a risk some non-runtime memory could be visible to the OS view.
255 if (((FixedPcdGet32 (PcdHypFvSize) & EFI_PAGE_MASK) == 0) && ((FixedPcdGet32 (PcdHypFvBaseAddress) & EFI_PAGE_MASK) == 0)) {
256 // The memory needs to be declared because the DXE core marked it as reserved and removed it from the memory space
257 // as it contains the Firmware.
258 Status = gDS->AddMemorySpace (
259 EfiGcdMemoryTypeSystemMemory,
260 FixedPcdGet32 (PcdHypFvBaseAddress), FixedPcdGet32 (PcdHypFvSize),
261 EFI_MEMORY_WB | EFI_MEMORY_RUNTIME
262 );
263 if (!EFI_ERROR (Status)) {
264 // We allocate the memory to ensure it is marked as runtime memory
265 HypBase = FixedPcdGet32 (PcdHypFvBaseAddress);
266 Status = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode,
267 EFI_SIZE_TO_PAGES (FixedPcdGet32 (PcdHypFvSize)), &HypBase);
268 }
269 } else {
270 // The hypervisor must be contained into a EFI_PAGE_SIZE-aligned region and its size must also be aligned
271 // on a EFI_PAGE_SIZE boundary (ie: 4KB).
272 Status = EFI_UNSUPPORTED;
273 ASSERT_EFI_ERROR (Status);
274 }
275
276 if (EFI_ERROR (Status)) {
277 return Status;
278 }
279 }
280
281 //
282 // Create an event belonging to the "gEfiEndOfDxeEventGroupGuid" group.
283 // The "OnEndOfDxe()" function is declared as the call back function.
284 // It will be called at the end of the DXE phase when an event of the
285 // same group is signalled to inform about the end of the DXE phase.
286 // Install the INSTALL_FDT_PROTOCOL protocol.
287 //
288 Status = gBS->CreateEventEx (
289 EVT_NOTIFY_SIGNAL,
290 TPL_CALLBACK,
291 OnEndOfDxe,
292 NULL,
293 &gEfiEndOfDxeEventGroupGuid,
294 &EndOfDxeEvent
295 );
296
297 // Install dynamic Shell command to run baremetal binaries.
298 Status = ShellDynCmdRunAxfInstall (ImageHandle);
299 if (EFI_ERROR (Status)) {
300 DEBUG ((EFI_D_ERROR, "ArmJunoDxe: Failed to install ShellDynCmdRunAxf\n"));
301 }
302
303 //
304 // We detect whether we are running on a Juno r0 or Juno r1 board at
305 // runtime by checking the value of the MIDR register.
306 //
307
308 Midr = ArmReadMidr ();
309 CpuType = (Midr >> ARM_CPU_TYPE_SHIFT) & ARM_CPU_TYPE_MASK;
310 CpuRev = Midr & ARM_CPU_REV_MASK;
311
312 switch (CpuType) {
313 case ARM_CPU_TYPE_A53:
314 if (CpuRev == ARM_CPU_REV (0, 0)) {
315 JunoRevision = JUNO_R0;
316 } else if (CpuRev == ARM_CPU_REV (0, 3)) {
317 JunoRevision = JUNO_R1;
318 }
319 break;
320
321 case ARM_CPU_TYPE_A57:
322 if (CpuRev == ARM_CPU_REV (0, 0)) {
323 JunoRevision = JUNO_R0;
324 } else if (CpuRev == ARM_CPU_REV (1, 1)) {
325 JunoRevision = JUNO_R1;
326 }
327 }
328
329 //
330 // Try to install the ACPI Tables
331 //
332 if (JunoRevision == JUNO_R0) {
333 Status = LocateAndInstallAcpiFromFvConditional (&mJunoAcpiTableFile, AcpiTableJunoR0Check);
334 } else if (JunoRevision == JUNO_R1) {
335 Status = LocateAndInstallAcpiFromFvConditional (&mJunoAcpiTableFile, AcpiTableJunoR1Check);
336 }
337 ASSERT_EFI_ERROR (Status);
338
339
340 //
341 // Set the R1 two boot options if not already done.
342 //
343 if (JunoRevision == JUNO_R1) {
344 Status = SetJunoR1DefaultBootEntries ();
345 if (EFI_ERROR (Status)) {
346 return Status;
347 }
348
349 // Enable PCI enumeration
350 PcdSetBool (PcdPciDisableBusEnumeration, FALSE);
351
352 // Declare the related ACPI Tables
353 EfiCreateProtocolNotifyEvent (
354 &gEfiAcpiTableProtocolGuid,
355 TPL_CALLBACK,
356 AcpiPciNotificationEvent,
357 NULL,
358 &mAcpiRegistration
359 );
360 }
361
362 //
363 // Set up the device path to the FDT.
364 //
365 switch (JunoRevision) {
366 case JUNO_R0:
367 TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoR0FdtDevicePath);
368 break;
369
370 case JUNO_R1:
371 TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoR1A57x2FdtDevicePath);
372 break;
373
374 default:
375 TextDevicePath = NULL;
376 }
377
378 if (TextDevicePath != NULL) {
379 TextDevicePathSize = StrSize (TextDevicePath);
380 Buffer = PcdSetPtr (PcdFdtDevicePaths, &TextDevicePathSize, TextDevicePath);
381 Status = (Buffer != NULL) ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL;
382 } else {
383 Status = EFI_NOT_FOUND;
384 }
385
386 if (EFI_ERROR (Status)) {
387 DEBUG (
388 (EFI_D_ERROR,
389 "ArmJunoDxe: Setting of FDT device path in PcdFdtDevicePaths failed - %r\n", Status)
390 );
391 return Status;
392 }
393
394 return Status;
395 }
396
397 /**
398 * If no boot entry is currently defined, define the two default boot entries
399 * for Juno R1.
400 *
401 * @return EFI_SUCCESS Some boot entries were already defined or
402 * the default boot entries were set successfully.
403 * @return EFI_OUT_OF_RESOURCES A memory allocation failed.
404 * @return EFI_DEVICE_ERROR An UEFI variable could not be saved due to a hardware failure.
405 * @return EFI_WRITE_PROTECTED An UEFI variable is read-only.
406 * @return EFI_SECURITY_VIOLATION An UEFI variable could not be written.
407 */
408 STATIC
409 EFI_STATUS
SetJunoR1DefaultBootEntries(VOID)410 SetJunoR1DefaultBootEntries (
411 VOID
412 )
413 {
414 EFI_STATUS Status;
415 CONST CHAR16* ExtraBootArgument = L" dtb=r1a57a53.dtb";
416 UINTN Size;
417 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
418 EFI_DEVICE_PATH* BootDevicePath;
419 UINT32 SysPciGbeL;
420 UINT32 SysPciGbeH;
421 CHAR16* DefaultBootArgument;
422 CHAR16* DefaultBootArgument1;
423 UINTN DefaultBootArgument1Size;
424 CHAR16* DefaultBootArgument2;
425 UINTN DefaultBootArgument2Size;
426 UINT16 BootOrder[2];
427
428 BootDevicePath = NULL;
429 DefaultBootArgument1 = NULL;
430 DefaultBootArgument2 = NULL;
431
432 //
433 // Because the driver has a dependency on gEfiVariable(Write)ArchProtocolGuid
434 // (see [Depex] section of the INF file), we know we can safely access the
435 // UEFI Variable at that stage.
436 //
437 Size = 0;
438 Status = gRT->GetVariable (L"BootOrder", &gEfiGlobalVariableGuid, NULL, &Size, NULL);
439 if (Status != EFI_NOT_FOUND) {
440 return EFI_SUCCESS;
441 }
442
443 Status = gBS->LocateProtocol (
444 &gEfiDevicePathFromTextProtocolGuid,
445 NULL,
446 (VOID **)&EfiDevicePathFromTextProtocol
447 );
448 if (EFI_ERROR (Status)) {
449 //
450 // You must provide an implementation of DevicePathFromTextProtocol
451 // in your firmware (eg: DevicePathDxe)
452 //
453 DEBUG ((EFI_D_ERROR, "Error: Require DevicePathFromTextProtocol\n"));
454 return Status;
455 }
456 //
457 // We use the same default kernel.
458 //
459 BootDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (
460 (CHAR16*)PcdGetPtr (PcdDefaultBootDevicePath)
461 );
462 if (BootDevicePath == NULL) {
463 return EFI_UNSUPPORTED;
464 }
465
466 DefaultBootArgument = (CHAR16*)PcdGetPtr (PcdDefaultBootArgument);
467 DefaultBootArgument1Size = StrSize (DefaultBootArgument) +
468 (SKY2_MAC_ADDRESS_BOOTARG_LEN * sizeof (CHAR16));
469 DefaultBootArgument2Size = DefaultBootArgument1Size + StrSize (ExtraBootArgument);
470
471 Status = EFI_OUT_OF_RESOURCES;
472 DefaultBootArgument1 = AllocatePool (DefaultBootArgument1Size);
473 if (DefaultBootArgument1 == NULL) {
474 goto Error;
475 }
476 DefaultBootArgument2 = AllocatePool (DefaultBootArgument2Size);
477 if (DefaultBootArgument2 == NULL) {
478 goto Error;
479 }
480
481 SysPciGbeL = MmioRead32 (ARM_JUNO_SYS_PCIGBE_L);
482 SysPciGbeH = MmioRead32 (ARM_JUNO_SYS_PCIGBE_H);
483
484 UnicodeSPrint (
485 DefaultBootArgument1,
486 DefaultBootArgument1Size,
487 L"%s sky2.mac_address=0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x",
488 DefaultBootArgument,
489 (SysPciGbeH >> 8 ) & 0xFF, (SysPciGbeH ) & 0xFF,
490 (SysPciGbeL >> 24) & 0xFF, (SysPciGbeL >> 16) & 0xFF,
491 (SysPciGbeL >> 8 ) & 0xFF, (SysPciGbeL ) & 0xFF
492 );
493
494 CopyMem (DefaultBootArgument2, DefaultBootArgument1, DefaultBootArgument1Size);
495 CopyMem (
496 (UINT8*)DefaultBootArgument2 + DefaultBootArgument1Size - sizeof (CHAR16),
497 ExtraBootArgument,
498 StrSize (ExtraBootArgument)
499 );
500
501 //
502 // Create Boot0001 environment variable
503 //
504 Status = BootOptionCreate (
505 L"Boot0001", LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT,
506 L"Linux with A57x2", BootDevicePath,
507 (UINT8*)DefaultBootArgument1, DefaultBootArgument1Size
508 );
509 if (EFI_ERROR (Status)) {
510 ASSERT_EFI_ERROR (Status);
511 goto Error;
512 }
513
514 //
515 // Create Boot0002 environment variable
516 //
517 Status = BootOptionCreate (
518 L"Boot0002", LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT,
519 L"Linux with A57x2_A53x4", BootDevicePath,
520 (UINT8*)DefaultBootArgument2, DefaultBootArgument2Size
521 );
522 if (EFI_ERROR (Status)) {
523 ASSERT_EFI_ERROR (Status);
524 goto Error;
525 }
526
527 //
528 // Add the new Boot Index to the list
529 //
530 BootOrder[0] = 1; // Boot0001
531 BootOrder[1] = 2; // Boot0002
532 Status = gRT->SetVariable (
533 L"BootOrder",
534 &gEfiGlobalVariableGuid,
535 EFI_VARIABLE_NON_VOLATILE |
536 EFI_VARIABLE_BOOTSERVICE_ACCESS |
537 EFI_VARIABLE_RUNTIME_ACCESS,
538 sizeof (BootOrder),
539 BootOrder
540 );
541
542 Error:
543 if (BootDevicePath != NULL) {
544 FreePool (BootDevicePath);
545 }
546 if (DefaultBootArgument1 != NULL) {
547 FreePool (DefaultBootArgument1);
548 }
549 if (DefaultBootArgument2 != NULL) {
550 FreePool (DefaultBootArgument2);
551 }
552
553 if (EFI_ERROR (Status)) {
554 DEBUG ((
555 EFI_D_ERROR,
556 "ArmJunoDxe - The setting of the default boot entries failed - %r\n",
557 Status
558 ));
559 }
560
561 return Status;
562 }
563