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