]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Library/BdsLib/BdsLinuxFdt.c
ArmPkg/BdsLib: Update 'cpu-release-addr' with the UEFI locations if the method is...
[mirror_edk2.git] / ArmPkg / Library / BdsLib / BdsLinuxFdt.c
index fa06287b1967cf0e0982afe6aa18db0663c8dd2f..5fccf8cac791152370e44fb1daa8f40771347080 100644 (file)
 *\r
 **/\r
 \r
+#include <Library/ArmSmcLib.h>\r
 #include <Library/PcdLib.h>\r
 #include <libfdt.h>\r
 \r
+#include <IndustryStandard/ArmSmc.h>\r
+\r
 #include "BdsInternal.h"\r
 #include "BdsLinuxLoader.h"\r
 \r
@@ -104,6 +107,23 @@ DebugDumpFdt (
   UINTN shift;\r
   UINT32 version;\r
 \r
+  {\r
+    // Can 'memreserve' be printed by below code?\r
+    INTN num = fdt_num_mem_rsv(FdtBlob);\r
+    INTN i, err;\r
+    UINT64 addr = 0,size = 0;\r
+\r
+    for (i = 0; i < num; i++) {\r
+      err = fdt_get_mem_rsv(FdtBlob, i, &addr, &size);\r
+      if (err) {\r
+        DEBUG((EFI_D_ERROR, "Error (%d) : Cannot get memreserve section (%d)\n", err, i));\r
+      }\r
+      else {\r
+        Print(L"/memreserve/ \t0x%lx \t0x%lx;\n",addr,size);\r
+      }\r
+    }\r
+  }\r
+\r
   depth = 0;\r
   shift = 4;\r
 \r
@@ -159,6 +179,25 @@ DebugDumpFdt (
   }\r
 }\r
 \r
+STATIC\r
+BOOLEAN\r
+IsLinuxReservedRegion (\r
+  IN EFI_MEMORY_TYPE MemoryType\r
+  )\r
+{\r
+  switch(MemoryType) {\r
+  case EfiRuntimeServicesCode:\r
+  case EfiRuntimeServicesData:\r
+  case EfiUnusableMemory:\r
+  case EfiACPIReclaimMemory:\r
+  case EfiACPIMemoryNVS:\r
+    return TRUE;\r
+  default:\r
+    return FALSE;\r
+  }\r
+}\r
+\r
+\r
 typedef struct {\r
   UINTN   Base;\r
   UINTN   Size;\r
@@ -170,7 +209,7 @@ PrepareFdt (
   IN     EFI_PHYSICAL_ADDRESS InitrdImage,\r
   IN     UINTN                InitrdImageSize,\r
   IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase,\r
-  IN OUT UINT32               *FdtBlobSize\r
+  IN OUT UINT               *FdtBlobSize\r
   )\r
 {\r
   EFI_STATUS            Status;\r
@@ -182,6 +221,7 @@ PrepareFdt (
   INTN                  cpu_node;\r
   INTN                  lenp;\r
   CONST VOID*           BootArg;\r
+  CONST VOID*           Method;\r
   EFI_PHYSICAL_ADDRESS  InitrdImageStart;\r
   EFI_PHYSICAL_ADDRESS  InitrdImageEnd;\r
   FdtRegion             Region;\r
@@ -195,6 +235,41 @@ PrepareFdt (
   UINT32                ClusterId;\r
   UINT32                CoreId;\r
   UINT64                CpuReleaseAddr;\r
+  UINTN                 MemoryMapSize;\r
+  EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
+  UINTN                 MapKey;\r
+  UINTN                 DescriptorSize;\r
+  UINT32                DescriptorVersion;\r
+  UINTN                 Pages;\r
+  BOOLEAN               PsciSmcSupported;\r
+  UINTN                 Rx;\r
+\r
+  //\r
+  // Ensure the Power State Coordination Interface (PSCI) SMCs are there if supported\r
+  //\r
+  PsciSmcSupported = FALSE;\r
+  if (FeaturePcdGet (PcdArmPsciSupport) == TRUE) {\r
+    // Check the SMC response to the Presence SMC\r
+    Rx   = ARM_SMC_ID_PRESENCE;\r
+    ArmCallSmc (&Rx);\r
+    if (Rx == 1) {\r
+      // Check the SMC UID\r
+      Rx   = ARM_SMC_ID_UID;\r
+      ArmCallSmc (&Rx);\r
+      if (Rx == ARM_TRUSTZONE_UID_4LETTERID) {\r
+        Rx   = ARM_SMC_ID_UID + 1;\r
+        ArmCallSmc (&Rx);\r
+        //TODO: Replace ARM magic number\r
+        if (Rx == 0x40524d48) {\r
+          PsciSmcSupported = TRUE;\r
+        }\r
+      }\r
+      if (PsciSmcSupported == FALSE) {\r
+        DEBUG((EFI_D_ERROR,"Warning: The Power State Coordination Interface (PSCI) is not supported"\r
+                           "by your platform Trusted Firmware.\n"));\r
+      }\r
+    }\r
+  }\r
 \r
   err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase));\r
   if (err != 0) {\r
@@ -259,7 +334,9 @@ PrepareFdt (
     }\r
   DEBUG_CODE_END();\r
 \r
+  //\r
   // Set Linux CmdLine\r
+  //\r
   if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) {\r
     err = fdt_setprop(fdt, node, "bootargs", CommandLineArguments, AsciiStrSize(CommandLineArguments));\r
     if (err) {\r
@@ -267,7 +344,9 @@ PrepareFdt (
     }\r
   }\r
 \r
+  //\r
   // Set Linux Initrd\r
+  //\r
   if (InitrdImageSize != 0) {\r
     InitrdImageStart = cpu_to_fdt64 (InitrdImage);\r
     err = fdt_setprop(fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof(EFI_PHYSICAL_ADDRESS));\r
@@ -281,7 +360,9 @@ PrepareFdt (
     }\r
   }\r
 \r
+  //\r
   // Set Physical memory setup if does not exist\r
+  //\r
   node = fdt_subnode_offset(fdt, 0, "memory");\r
   if (node < 0) {\r
     // The 'memory' node does not exist, create it\r
@@ -308,7 +389,39 @@ PrepareFdt (
     }\r
   }\r
 \r
+  //\r
+  // Add the memory regions reserved by the UEFI Firmware\r
+  //\r
+\r
+  // Retrieve the UEFI Memory Map\r
+  MemoryMap = NULL;\r
+  MemoryMapSize = 0;\r
+  Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);\r
+  if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1;\r
+    MemoryMap = AllocatePages (Pages);\r
+    Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);\r
+  }\r
+\r
+  // Go through the list and add the reserved region to the Device Tree\r
+  if (!EFI_ERROR(Status)) {\r
+    for (Index = 0; Index < (MemoryMapSize / sizeof(EFI_MEMORY_DESCRIPTOR)); Index++) {\r
+      if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE)MemoryMap[Index].Type)) {\r
+        DEBUG((DEBUG_VERBOSE, "Reserved region of type %d [0x%X, 0x%X]\n",\r
+            MemoryMap[Index].Type,\r
+            (UINTN)MemoryMap[Index].PhysicalStart,\r
+            (UINTN)(MemoryMap[Index].PhysicalStart + MemoryMap[Index].NumberOfPages * EFI_PAGE_SIZE)));\r
+        err = fdt_add_mem_rsv(fdt, MemoryMap[Index].PhysicalStart, MemoryMap[Index].NumberOfPages * EFI_PAGE_SIZE);\r
+        if (err != 0) {\r
+          Print(L"Warning: Fail to add 'memreserve' (err:%d)\n", err);\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
   // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms\r
+  //\r
   for (Index=0; Index < gST->NumberOfTableEntries; Index++) {\r
     // Check for correct GUID type\r
     if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {\r
@@ -338,19 +451,53 @@ PrepareFdt (
           fdt_setprop(fdt, cpu_node, "reg", &Index, sizeof(Index));\r
         }\r
 \r
-        fdt_setprop_string(fdt, cpu_node, "enable-method", "spin-table");\r
-        CpuReleaseAddr = cpu_to_fdt64(ArmCoreInfoTable[Index].MailboxSetAddress);\r
-        fdt_setprop(fdt, cpu_node, "cpu-release-addr", &CpuReleaseAddr, sizeof(CpuReleaseAddr));\r
-\r
-        // If it is not the primary core than the cpu should be disabled\r
-        if (((ArmCoreInfoTable[Index].ClusterId != ClusterId) || (ArmCoreInfoTable[Index].CoreId != CoreId))) {\r
-          fdt_setprop_string(fdt, cpu_node, "status", "disabled");\r
+        // If Power State Coordination Interface (PSCI) is not supported then it is expected the secondary\r
+        // cores are spinning waiting for the Operating System to release them\r
+        if (PsciSmcSupported == FALSE) {\r
+          // We as the bootloader are responsible for either creating or updating\r
+          // these entries. Do not trust the entries in the DT. We only know about\r
+          // 'spin-table' type. Do not try to update other types if defined.\r
+          Method = fdt_getprop(fdt, cpu_node, "enable-method", &lenp);\r
+          if ( (Method == NULL) || (!AsciiStrCmp((CHAR8 *)Method, "spin-table")) ) {\r
+            fdt_setprop_string(fdt, cpu_node, "enable-method", "spin-table");\r
+            CpuReleaseAddr = cpu_to_fdt64(ArmCoreInfoTable[Index].MailboxSetAddress);\r
+            fdt_setprop(fdt, cpu_node, "cpu-release-addr", &CpuReleaseAddr, sizeof(CpuReleaseAddr));\r
+\r
+            // If it is not the primary core than the cpu should be disabled\r
+            if (((ArmCoreInfoTable[Index].ClusterId != ClusterId) || (ArmCoreInfoTable[Index].CoreId != CoreId))) {\r
+              fdt_setprop_string(fdt, cpu_node, "status", "disabled");\r
+            }\r
+          } else {\r
+            Print(L"Warning: Unsupported enable-method type for CPU[%d] : %a\n", Index, (CHAR8 *)Method);\r
+          }\r
         }\r
       }\r
       break;\r
     }\r
   }\r
 \r
+  // If the Power State Coordination Interface is supported then we signal it in the Device Tree\r
+  if (PsciSmcSupported == TRUE) {\r
+    // Before to create it we check if the node is not already defined in the Device Tree\r
+    node = fdt_subnode_offset(fdt, 0, "psci");\r
+    if (node < 0) {\r
+      // The 'psci' node does not exist, create it\r
+      node = fdt_add_subnode(fdt, 0, "psci");\r
+      if (node < 0) {\r
+        DEBUG((EFI_D_ERROR,"Error on creating 'psci' node\n"));\r
+        Status = EFI_INVALID_PARAMETER;\r
+        goto FAIL_NEW_FDT;\r
+      } else {\r
+        fdt_setprop_string(fdt, node, "compatible", "arm,psci");\r
+        fdt_setprop_string(fdt, node, "method", "smc");\r
+        fdt_setprop_cell(fdt, node, "cpu_suspend", ARM_SMC_ARM_CPU_SUSPEND);\r
+        fdt_setprop_cell(fdt, node, "cpu_off", ARM_SMC_ARM_CPU_OFF);\r
+        fdt_setprop_cell(fdt, node, "cpu_on", ARM_SMC_ARM_CPU_ON);\r
+        fdt_setprop_cell(fdt, node, "cpu_migrate", ARM_SMC_ARM_MIGRATE);\r
+      }\r
+    }\r
+  }\r
+\r
   DEBUG_CODE_BEGIN();\r
     //DebugDumpFdt (fdt);\r
   DEBUG_CODE_END();\r