Home | History | Annotate | Download | only in FdtPlatformDxe
      1 /** @file
      2 
      3   Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>
      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 "FdtPlatform.h"
     16 
     17 #include <Library/PcdLib.h>
     18 #include <Library/DevicePathLib.h>
     19 #include <Library/BdsLib.h>
     20 
     21 #include <Protocol/DevicePath.h>
     22 
     23 //
     24 // Internal variables
     25 //
     26 
     27 STATIC CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mShellDynCmdProtocolSetFdt = {
     28     L"setfdt",                // Name of the command
     29     ShellDynCmdSetFdtHandler, // Handler
     30     ShellDynCmdSetFdtGetHelp  // GetHelp
     31 };
     32 
     33 STATIC CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mShellDynCmdProtocolDumpFdt = {
     34     L"dumpfdt",                // Name of the command
     35     ShellDynCmdDumpFdtHandler, // Handler
     36     ShellDynCmdDumpFdtGetHelp  // GetHelp
     37 };
     38 
     39 STATIC CONST EFI_GUID  mFdtPlatformDxeHiiGuid = {
     40                          0x8afa7610, 0x62b1, 0x46aa,
     41                          {0xb5, 0x34, 0xc3, 0xde, 0xff, 0x39, 0x77, 0x8c}
     42                          };
     43 
     44 EFI_HANDLE mFdtPlatformDxeHiiHandle;
     45 
     46 /**
     47   Install the FDT specified by its device path in text form.
     48 
     49   @param[in]  TextDevicePath  Device path of the FDT to install in text form
     50 
     51   @retval  EFI_SUCCESS            The FDT was installed.
     52   @retval  EFI_NOT_FOUND          Failed to locate a protocol or a file.
     53   @retval  EFI_INVALID_PARAMETER  Invalid device path.
     54   @retval  EFI_UNSUPPORTED        Device path not supported.
     55   @retval  EFI_OUT_OF_RESOURCES   An allocation failed.
     56 **/
     57 STATIC
     58 EFI_STATUS
     59 InstallFdt (
     60   IN CONST CHAR16*  TextDevicePath
     61   )
     62 {
     63   EFI_STATUS                          Status;
     64   EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL  *EfiDevicePathFromTextProtocol;
     65   EFI_DEVICE_PATH                     *DevicePath;
     66   EFI_PHYSICAL_ADDRESS                FdtBlobBase;
     67   UINTN                               FdtBlobSize;
     68   UINTN                               NumPages;
     69   EFI_PHYSICAL_ADDRESS                FdtConfigurationTableBase;
     70 
     71   Status = gBS->LocateProtocol (
     72                   &gEfiDevicePathFromTextProtocolGuid,
     73                   NULL,
     74                   (VOID **)&EfiDevicePathFromTextProtocol
     75                   );
     76   if (EFI_ERROR (Status)) {
     77     DEBUG ((EFI_D_ERROR, "InstallFdt() - Failed to locate EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL protocol\n"));
     78     return Status;
     79   }
     80 
     81   DevicePath = (EFI_DEVICE_PATH*)EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (TextDevicePath);
     82   if (DevicePath == NULL) {
     83     return EFI_INVALID_PARAMETER;
     84   }
     85 
     86   //
     87   // Load the FDT given its device path.
     88   // This operation may fail if the device path is not supported.
     89   //
     90   FdtBlobBase = 0;
     91   NumPages    = 0;
     92   Status = BdsLoadImage (DevicePath, AllocateAnyPages, &FdtBlobBase, &FdtBlobSize);
     93   if (EFI_ERROR (Status)) {
     94     goto Error;
     95   }
     96 
     97   //
     98   // Ensure that the FDT header is valid and that the Size of the Device Tree
     99   // is smaller than the size of the read file
    100   //
    101   if (fdt_check_header ((VOID*)(UINTN)FdtBlobBase) != 0 ||
    102       (UINTN)fdt_totalsize ((VOID*)(UINTN)FdtBlobBase) > FdtBlobSize) {
    103     DEBUG ((EFI_D_ERROR, "InstallFdt() - loaded FDT binary image seems corrupt\n"));
    104     Status = EFI_LOAD_ERROR;
    105     goto Error;
    106   }
    107 
    108   //
    109   // Store the FDT as Runtime Service Data to prevent the Kernel from
    110   // overwritting its data.
    111   //
    112   NumPages = EFI_SIZE_TO_PAGES (FdtBlobSize);
    113   Status = gBS->AllocatePages (
    114                   AllocateAnyPages, EfiRuntimeServicesData,
    115                   NumPages, &FdtConfigurationTableBase
    116                   );
    117   if (EFI_ERROR (Status)) {
    118     goto Error;
    119   }
    120   CopyMem (
    121     (VOID*)(UINTN)FdtConfigurationTableBase,
    122     (VOID*)(UINTN)FdtBlobBase,
    123     FdtBlobSize
    124     );
    125 
    126   //
    127   // Install the FDT into the Configuration Table
    128   //
    129   Status = gBS->InstallConfigurationTable (
    130                   &gFdtTableGuid,
    131                   (VOID*)(UINTN)FdtConfigurationTableBase
    132                   );
    133   if (EFI_ERROR (Status)) {
    134     gBS->FreePages (FdtConfigurationTableBase, NumPages);
    135   }
    136 
    137 Error:
    138   if (FdtBlobBase != 0) {
    139     gBS->FreePages (FdtBlobBase, NumPages);
    140   }
    141   FreePool (DevicePath);
    142 
    143   return Status;
    144 }
    145 
    146 /**
    147   Main entry point of the FDT platform driver.
    148 
    149   @param[in]  ImageHandle   The firmware allocated handle for the present driver
    150                             UEFI image.
    151   @param[in]  *SystemTable  A pointer to the EFI System table.
    152 
    153   @retval  EFI_SUCCESS           The driver was initialized.
    154   @retval  EFI_OUT_OF_RESOURCES  The "End of DXE" event could not be allocated or
    155                                  there was not enough memory in pool to install
    156                                  the Shell Dynamic Command protocol.
    157   @retval  EFI_LOAD_ERROR        Unable to add the HII package.
    158 
    159 **/
    160 EFI_STATUS
    161 FdtPlatformEntryPoint (
    162   IN EFI_HANDLE        ImageHandle,
    163   IN EFI_SYSTEM_TABLE  *SystemTable
    164   )
    165 {
    166   EFI_STATUS  Status;
    167   EFI_HANDLE  Handle;
    168 
    169   //
    170   // Install the Device Tree from its expected location
    171   //
    172   Status = RunFdtInstallation (NULL);
    173 
    174   if (FeaturePcdGet (PcdOverridePlatformFdt) || FeaturePcdGet (PcdDumpFdtShellCommand)) {
    175     //
    176     // Register the strings for the user interface in the HII Database.
    177     // This shows the way to the multi-language support, even if
    178     // only the English language is actually supported. The strings to register
    179     // are stored in the "ShellSetFdtStrings[]" array. This array is
    180     // built by the building process from the "*.uni" file associated to
    181     // the present driver (cf. FdtPlatfromDxe.inf). Examine your Build
    182     // folder under your package's DEBUG folder and you will find the array
    183     // defined in a xxxStrDefs.h file.
    184     //
    185     mFdtPlatformDxeHiiHandle = HiiAddPackages (
    186                                  &mFdtPlatformDxeHiiGuid,
    187                                  ImageHandle,
    188                                  FdtPlatformDxeStrings,
    189                                  NULL
    190                                  );
    191   }
    192 
    193   //
    194   // If the development features are enabled, install the dynamic shell
    195   // command "setfdt" to be able to define a device path for the FDT
    196   // that has precedence over the device paths defined by
    197   // "PcdFdtDevicePaths".
    198   //
    199 
    200   if (FeaturePcdGet (PcdOverridePlatformFdt)) {
    201     if (mFdtPlatformDxeHiiHandle != NULL) {
    202       // We install dynamic EFI command on separate handles as we cannot register
    203       // more than one protocol of the same protocol interface on the same handle.
    204       Handle = NULL;
    205       Status = gBS->InstallMultipleProtocolInterfaces (
    206                       &Handle,
    207                       &gEfiShellDynamicCommandProtocolGuid,
    208                       &mShellDynCmdProtocolSetFdt,
    209                       NULL
    210                       );
    211       if (EFI_ERROR (Status)) {
    212         HiiRemovePackages (mFdtPlatformDxeHiiHandle);
    213       }
    214     } else {
    215       Status = EFI_LOAD_ERROR;
    216     }
    217     if (EFI_ERROR (Status)) {
    218       DEBUG ((
    219         EFI_D_WARN,
    220         "Unable to install \"setfdt\" EFI Shell command - %r \n",
    221         Status
    222         ));
    223     }
    224   }
    225 
    226   if (FeaturePcdGet (PcdDumpFdtShellCommand)) {
    227     if (mFdtPlatformDxeHiiHandle != NULL) {
    228       // We install dynamic EFI command on separate handles as we cannot register
    229       // more than one protocol of the same protocol interface on the same handle.
    230       Handle = NULL;
    231       Status = gBS->InstallMultipleProtocolInterfaces (
    232                       &Handle,
    233                       &gEfiShellDynamicCommandProtocolGuid,
    234                       &mShellDynCmdProtocolDumpFdt,
    235                       NULL
    236                       );
    237       if (EFI_ERROR (Status)) {
    238         HiiRemovePackages (mFdtPlatformDxeHiiHandle);
    239       }
    240     } else {
    241       Status = EFI_LOAD_ERROR;
    242     }
    243     if (EFI_ERROR (Status)) {
    244       DEBUG ((
    245         EFI_D_WARN,
    246         "Unable to install \"dumpfdt\" EFI Shell command - %r \n",
    247         Status
    248         ));
    249     }
    250   }
    251 
    252   return Status;
    253 }
    254 
    255 /**
    256   Run the FDT installation process.
    257 
    258   Loop in priority order over the device paths from which the FDT has
    259   been asked to be retrieved for. For each device path, try to install
    260   the FDT. Stop as soon as an installation succeeds.
    261 
    262   @param[in]  SuccessfullDevicePath  If not NULL, address where to store the
    263                                      pointer to the text device path from
    264                                      which the FDT was successfully retrieved.
    265                                      Not used if the FDT installation failed.
    266                                      The returned address is the address of
    267                                      an allocated buffer that has to be
    268                                      freed by the caller.
    269 
    270   @retval  EFI_SUCCESS            The FDT was installed.
    271   @retval  EFI_NOT_FOUND          Failed to locate a protocol or a file.
    272   @retval  EFI_INVALID_PARAMETER  Invalid device path.
    273   @retval  EFI_UNSUPPORTED        Device path not supported.
    274   @retval  EFI_OUT_OF_RESOURCES   An allocation failed.
    275 
    276 **/
    277 EFI_STATUS
    278 RunFdtInstallation (
    279   OUT CHAR16  **SuccessfullDevicePath
    280   )
    281 {
    282   EFI_STATUS  Status;
    283   UINTN       DataSize;
    284   CHAR16      *TextDevicePath;
    285   CHAR16      *TextDevicePathStart;
    286   CHAR16      *TextDevicePathSeparator;
    287   UINTN       TextDevicePathLen;
    288 
    289   TextDevicePath = NULL;
    290   //
    291   // For development purpose, if enabled through the "PcdOverridePlatformFdt"
    292   // feature PCD, try first to install the FDT specified by the device path in
    293   // text form stored in the "Fdt" UEFI variable.
    294   //
    295   if (FeaturePcdGet (PcdOverridePlatformFdt)) {
    296     DataSize = 0;
    297     Status = gRT->GetVariable (
    298                     L"Fdt",
    299                     &gFdtVariableGuid,
    300                     NULL,
    301                     &DataSize,
    302                     NULL
    303                     );
    304 
    305     //
    306     // Keep going only if the "Fdt" variable is defined.
    307     //
    308 
    309     if (Status == EFI_BUFFER_TOO_SMALL) {
    310       TextDevicePath = AllocatePool (DataSize);
    311       if (TextDevicePath == NULL) {
    312         Status = EFI_OUT_OF_RESOURCES;
    313         goto Error;
    314       }
    315 
    316       Status = gRT->GetVariable (
    317                       L"Fdt",
    318                       &gFdtVariableGuid,
    319                       NULL,
    320                       &DataSize,
    321                       TextDevicePath
    322                       );
    323       if (EFI_ERROR (Status)) {
    324         FreePool (TextDevicePath);
    325         goto Error;
    326       }
    327 
    328       Status = InstallFdt (TextDevicePath);
    329       if (!EFI_ERROR (Status)) {
    330         DEBUG ((
    331           EFI_D_WARN,
    332           "Installation of the FDT using the device path <%s> completed.\n",
    333           TextDevicePath
    334           ));
    335         goto Done;
    336       }
    337       DEBUG ((
    338         EFI_D_ERROR,
    339         "Installation of the FDT specified by the \"Fdt\" UEFI variable failed - %r\n",
    340         Status
    341         ));
    342       FreePool (TextDevicePath);
    343     }
    344   }
    345 
    346   //
    347   // Loop over the device path list provided by "PcdFdtDevicePaths". The device
    348   // paths are in text form and separated by a semi-colon.
    349   //
    350 
    351   Status = EFI_NOT_FOUND;
    352   for (TextDevicePathStart = (CHAR16*)PcdGetPtr (PcdFdtDevicePaths);
    353        *TextDevicePathStart != L'\0'                               ; ) {
    354     TextDevicePathSeparator = StrStr (TextDevicePathStart, L";");
    355 
    356     //
    357     // Last device path of the list
    358     //
    359     if (TextDevicePathSeparator == NULL) {
    360       TextDevicePathLen = StrLen (TextDevicePathStart);
    361     } else {
    362       TextDevicePathLen = (UINTN)(TextDevicePathSeparator - TextDevicePathStart);
    363     }
    364 
    365     TextDevicePath = AllocateCopyPool (
    366                        (TextDevicePathLen + 1) * sizeof (CHAR16),
    367                        TextDevicePathStart
    368                        );
    369     if (TextDevicePath == NULL) {
    370       Status = EFI_OUT_OF_RESOURCES;
    371       goto Error;
    372     }
    373     TextDevicePath[TextDevicePathLen] = L'\0';
    374 
    375     Status = InstallFdt (TextDevicePath);
    376     if (!EFI_ERROR (Status)) {
    377       DEBUG ((EFI_D_WARN, "Installation of the FDT using the device path <%s> completed.\n",
    378         TextDevicePath
    379         ));
    380       goto Done;
    381     }
    382 
    383     DEBUG ((EFI_D_WARN, "Installation of the FDT using the device path <%s> failed - %r.\n",
    384       TextDevicePath, Status
    385       ));
    386     FreePool (TextDevicePath);
    387 
    388     if (TextDevicePathSeparator == NULL) {
    389       goto Error;
    390     }
    391     TextDevicePathStart = TextDevicePathSeparator + 1;
    392   }
    393 
    394 Error:
    395 Done:
    396 
    397   if (EFI_ERROR (Status)) {
    398     DEBUG ((EFI_D_ERROR, "Failed to install the FDT - %r.\n", Status));
    399     return Status;
    400   }
    401 
    402   if (SuccessfullDevicePath != NULL) {
    403     *SuccessfullDevicePath = TextDevicePath;
    404   } else {
    405     FreePool (TextDevicePath);
    406   }
    407 
    408   return EFI_SUCCESS;
    409 }
    410 
    411 /**
    412   Transcode one of the EFI return code used by the model into an EFI Shell return code.
    413 
    414   @param[in]  Status  EFI return code.
    415 
    416   @return  Transcoded EFI Shell return code.
    417 
    418 **/
    419 SHELL_STATUS
    420 EfiCodeToShellCode (
    421   IN EFI_STATUS  Status
    422   )
    423 {
    424   SHELL_STATUS  ShellStatus;
    425 
    426   switch (Status) {
    427   case EFI_SUCCESS :
    428     ShellStatus = SHELL_SUCCESS;
    429     break;
    430 
    431   case EFI_INVALID_PARAMETER :
    432     ShellStatus = SHELL_INVALID_PARAMETER;
    433     break;
    434 
    435   case EFI_UNSUPPORTED :
    436     ShellStatus = SHELL_UNSUPPORTED;
    437     break;
    438 
    439   case EFI_DEVICE_ERROR :
    440     ShellStatus = SHELL_DEVICE_ERROR;
    441     break;
    442 
    443   case EFI_WRITE_PROTECTED    :
    444   case EFI_SECURITY_VIOLATION :
    445     ShellStatus = SHELL_ACCESS_DENIED;
    446     break;
    447 
    448   case EFI_OUT_OF_RESOURCES :
    449     ShellStatus = SHELL_OUT_OF_RESOURCES;
    450     break;
    451 
    452   case EFI_NOT_FOUND :
    453     ShellStatus = SHELL_NOT_FOUND;
    454     break;
    455 
    456   default :
    457     ShellStatus = SHELL_ABORTED;
    458   }
    459 
    460   return ShellStatus;
    461 }
    462