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//
43typedef enum {
44  UNKNOWN,
45  JUNO_R0,
46  JUNO_R1
47} JUNO_REVISION;
48
49//
50// Function prototypes
51//
52STATIC EFI_STATUS SetJunoR1DefaultBootEntries (
53  VOID
54  );
55
56// This GUID must match the FILE_GUID in ArmPlatformPkg/ArmJunoPkg/AcpiTables/AcpiTables.inf
57STATIC CONST EFI_GUID mJunoAcpiTableFile = { 0xa1dd808e, 0x1e95, 0x4399, { 0xab, 0xc0, 0x65, 0x3c, 0x82, 0xe8, 0x53, 0x0c } };
58
59typedef 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
65STATIC 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
91EFI_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 */
106STATIC
107EFI_STATUS
108BootOptionCreate (
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**/
176STATIC
177VOID
178OnEndOfDxe (
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
202STATIC
203BOOLEAN
204AcpiTableJunoR0Check (
205  IN  EFI_ACPI_DESCRIPTION_HEADER *AcpiHeader
206  )
207{
208  return TRUE;
209}
210
211STATIC
212BOOLEAN
213AcpiTableJunoR1Check (
214  IN  EFI_ACPI_DESCRIPTION_HEADER *AcpiHeader
215  )
216{
217  return TRUE;
218}
219
220EFI_STATUS
221EFIAPI
222ArmJunoEntryPoint (
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 */
408STATIC
409EFI_STATUS
410SetJunoR1DefaultBootEntries (
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
542Error:
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