Revision: 14487
          http://sourceforge.net/p/edk2/code/14487
Author:   oliviermartin
Date:     2013-07-18 18:13:02 +0000 (Thu, 18 Jul 2013)
Log Message:
-----------
ArmPkg/BdsLib: Added Aarch64 support for booting Linux

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Harry Liebel <[email protected]>
Signed-off-by: Olivier Martin <[email protected]>

Modified Paths:
--------------
    trunk/edk2/ArmPkg/ArmPkg.dec
    trunk/edk2/ArmPkg/ArmPkg.dsc
    trunk/edk2/ArmPkg/Library/BdsLib/BdsLib.inf

Added Paths:
-----------
    trunk/edk2/ArmPkg/Library/BdsLib/AArch64/
    trunk/edk2/ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c
    trunk/edk2/ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoaderHelper.S

Modified: trunk/edk2/ArmPkg/ArmPkg.dec
===================================================================
--- trunk/edk2/ArmPkg/ArmPkg.dec        2013-07-18 18:07:46 UTC (rev 14486)
+++ trunk/edk2/ArmPkg/ArmPkg.dec        2013-07-18 18:13:02 UTC (rev 14487)
@@ -190,3 +190,9 @@
   # Other modes include using SP0 or switching to Aarch32, but these are
   # not currently supported.
   gArmTokenSpaceGuid.PcdArmNonSecModeTransition|0x3c9|UINT32|0x0000003E
+  # If the fixed FDT address is not available, then it should be loaded above 
the kernel.
+  # The recommendation from the AArch64 Linux kernel is to have the FDT below 
512MB.
+  # (see the kernel doc: Documentation/arm64/booting.txt)
+  gArmTokenSpaceGuid.PcdArmLinuxFdtMaxOffset|0x20000000|UINT32|0x00000023
+  # The FDT blob must be loaded at a 2MB aligned address.
+  gArmTokenSpaceGuid.PcdArmLinuxFdtAlignment|0x00200000|UINT32|0x00000026

Modified: trunk/edk2/ArmPkg/ArmPkg.dsc
===================================================================
--- trunk/edk2/ArmPkg/ArmPkg.dsc        2013-07-18 18:07:46 UTC (rev 14486)
+++ trunk/edk2/ArmPkg/ArmPkg.dsc        2013-07-18 18:13:02 UTC (rev 14487)
@@ -129,7 +129,6 @@
 
   ArmPkg/Filesystem/SemihostFs/SemihostFs.inf
 
-  ArmPkg/Application/LinuxLoader/LinuxAtagLoader.inf
   ArmPkg/Application/LinuxLoader/LinuxFdtLoader.inf
 
 [Components.ARM]
@@ -146,6 +145,8 @@
   ArmPkg/Library/ArmLib/ArmV7/ArmV7LibSec.inf
   ArmPkg/Library/ArmLib/ArmV7/ArmV7LibPrePi.inf
 
+  ArmPkg/Application/LinuxLoader/LinuxAtagLoader.inf
+
 [Components.AARCH64]
   ArmPkg/Drivers/ArmCpuLib/ArmCortexAEMv8Lib/ArmCortexAEMv8Lib.inf
   ArmPkg/Drivers/ArmCpuLib/ArmCortexA5xLib/ArmCortexA5xLib.inf

Added: trunk/edk2/ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c
===================================================================
--- trunk/edk2/ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c                   
        (rev 0)
+++ trunk/edk2/ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c   2013-07-18 
18:13:02 UTC (rev 14487)
@@ -0,0 +1,353 @@
+/** @file
+*
+*  Copyright (c) 2011-2013, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD 
License
+*  which accompanies this distribution.  The full text of the license may be 
found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR 
IMPLIED.
+*
+**/
+#include <Library/ArmGicLib.h>
+#include <Ppi/ArmMpCoreInfo.h>
+#include <Library/IoLib.h>
+
+#include "BdsInternal.h"
+#include "BdsLinuxLoader.h"
+
+/*
+  Linux kernel booting: Look at the doc in the Kernel source :
+    Documentation/arm64/booting.txt
+  The kernel image must be placed at the start of the memory to be used by the
+  kernel (2MB aligned) + 0x80000.
+
+  The Device tree blob is expected to be under 2MB and be within the first 
512MB
+  of kernel memory and be 2MB aligned.
+
+  A Flattened Device Tree (FDT) used to boot linux needs to be updated before
+  the kernel is started. It needs to indicate how secondary cores are brought 
up
+  and where they are waiting before loading Linux. The FDT also needs to hold
+  the correct kernel command line and filesystem RAM-disk information.
+  At the moment we do not fully support generating this FDT information at
+  runtime. A prepared FDT should be provided at boot. FDT is the only supported
+  method for booting the AArch64 Linux kernel.
+
+  Linux does not use any runtime services at this time, so we can let it
+  overwrite UEFI.
+*/
+
+
+#define LINUX_ALIGN_VAL       (0x080000) // 2MB + 0x80000 mask
+#define LINUX_ALIGN_MASK      (0x1FFFFF) // Bottom 21bits
+#define ALIGN_2MB(addr)       ALIGN_POINTER(addr , (2*1024*1024))
+
+/* ARM32 and AArch64 kernel handover differ.
+ * x0 is set to FDT base.
+ * x1-x3 are reserved for future use and should be set to zero.
+ */
+typedef VOID (*LINUX_KERNEL64)(UINTN ParametersBase, UINTN Reserved0,
+                               UINTN Reserved1, UINTN Reserved2);
+
+/* These externs are used to relocate some ASM code into Linux memory. */
+extern VOID  *SecondariesPenStart;
+extern VOID  *SecondariesPenEnd;
+extern UINTN *AsmMailboxbase;
+
+
+STATIC
+EFI_STATUS
+PreparePlatformHardware (
+  VOID
+  )
+{
+  //Note: Interrupts will be disabled by the GIC driver when 
ExitBootServices() will be called.
+
+  // Clean before Disable else the Stack gets corrupted with old data.
+  ArmCleanDataCache ();
+  ArmDisableDataCache ();
+  // Invalidate all the entries that might have snuck in.
+  ArmInvalidateDataCache ();
+
+  // Disable and invalidate the instruction cache
+  ArmDisableInstructionCache ();
+  ArmInvalidateInstructionCache ();
+
+  // Turn off MMU
+  ArmDisableMmu();
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+StartLinux (
+  IN  EFI_PHYSICAL_ADDRESS  LinuxImage,
+  IN  UINTN                 LinuxImageSize,
+  IN  EFI_PHYSICAL_ADDRESS  FdtBlobBase,
+  IN  UINTN                 FdtBlobSize,
+  IN  UINT32                MachineType
+  )
+{
+  EFI_STATUS            Status;
+  LINUX_KERNEL64        LinuxKernel = (LINUX_KERNEL64)LinuxImage;
+
+  // Send msg to secondary cores to go to the kernel pen.
+  ArmGicSendSgiTo (PcdGet32(PcdGicDistributorBase), 
ARM_GIC_ICDSGIR_FILTER_EVERYONEELSE, 0x0E, PcdGet32 (PcdGicSgiIntId));
+
+  // Shut down UEFI boot services. ExitBootServices() will notify every driver 
that created an event on
+  // ExitBootServices event. Example the Interrupt DXE driver will disable the 
interrupts on this event.
+  Status = ShutdownUefiBootServices ();
+  if(EFI_ERROR(Status)) {
+    DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. 
Status=0x%X\n", Status));
+    goto Exit;
+  }
+
+  // Check if the Linux Image is a uImage
+  if (*(UINTN*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) {
+    // Assume the Image Entry Point is just after the uImage header (64-byte 
size)
+    LinuxKernel = (LINUX_KERNEL64)((UINTN)LinuxKernel + 64);
+    LinuxImageSize -= 64;
+  }
+
+  //
+  // Switch off interrupts, caches, mmu, etc
+  //
+  Status = PreparePlatformHardware ();
+  ASSERT_EFI_ERROR(Status);
+
+  // Register and print out performance information
+  PERF_END (NULL, "BDS", NULL, 0);
+  if (PerformanceMeasurementEnabled ()) {
+    PrintPerformance ();
+  }
+
+  //
+  // Start the Linux Kernel
+  //
+
+  // x1-x3 are reserved (set to zero) for future use.
+  LinuxKernel ((UINTN)FdtBlobBase, 0, 0, 0);
+
+  // Kernel should never exit
+  // After Life services are not provided
+  ASSERT(FALSE);
+
+Exit:
+  // Only be here if we fail to start Linux
+  Print (L"ERROR  : Can not start the kernel. Status=0x%X\n", Status);
+
+  // Free Runtimee Memory (kernel and FDT)
+  return Status;
+}
+
+
+/**
+  Start a Linux kernel from a Device Path
+
+  @param  LinuxKernel           Device Path to the Linux Kernel
+  @param  Parameters            Linux kernel agruments
+  @param  Fdt                   Device Path to the Flat Device Tree
+
+  @retval EFI_SUCCESS           All drivers have been connected
+  @retval EFI_NOT_FOUND         The Linux kernel Device Path has not been found
+  @retval EFI_OUT_OF_RESOURCES  There is not enough resource memory to store 
the matching results.
+
+**/
+EFI_STATUS
+BdsBootLinuxAtag (
+  IN  EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,
+  IN  CONST CHAR8*              Arguments
+  )
+{
+  // NOTE : AArch64 Linux kernel does not support ATAG, FDT only.
+  ASSERT(0);
+
+  return RETURN_UNSUPPORTED;
+}
+
+/**
+  Start a Linux kernel from a Device Path
+
+  @param  LinuxKernel           Device Path to the Linux Kernel
+  @param  Parameters            Linux kernel agruments
+  @param  Fdt                   Device Path to the Flat Device Tree
+
+  @retval EFI_SUCCESS           All drivers have been connected
+  @retval EFI_NOT_FOUND         The Linux kernel Device Path has not been found
+  @retval EFI_OUT_OF_RESOURCES  There is not enough resource memory to store 
the matching results.
+
+**/
+EFI_STATUS
+BdsBootLinuxFdt (
+  IN  EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,
+  IN  CONST CHAR8*              Arguments,
+  IN  EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath
+  )
+{
+  EFI_STATUS            Status;
+  EFI_STATUS            PenBaseStatus;
+  UINTN                 LinuxImageSize;
+  UINTN                 InitrdImageSize;
+  UINTN                 InitrdImageBaseSize;
+  UINTN                 FdtBlobSize;
+  EFI_PHYSICAL_ADDRESS  FdtBlobBase;
+  UINT32                FdtMachineType;
+  EFI_PHYSICAL_ADDRESS  LinuxImage;
+  EFI_PHYSICAL_ADDRESS  InitrdImage;
+  EFI_PHYSICAL_ADDRESS  InitrdImageBase;
+  ARM_PROCESSOR_TABLE   *ArmProcessorTable;
+  ARM_CORE_INFO         *ArmCoreInfoTable;
+  UINTN                 Index;
+  EFI_PHYSICAL_ADDRESS  PenBase;
+  UINTN                 PenSize;
+  UINTN                 MailBoxBase;
+
+  PenBaseStatus = EFI_UNSUPPORTED;
+  PenSize = 0;
+  FdtMachineType = 0xFFFFFFFF;
+  InitrdImage = 0;
+  InitrdImageSize = 0;
+  InitrdImageBase = 0;
+  InitrdImageBaseSize = 0;
+
+  PERF_START (NULL, "BDS", NULL, 0);
+
+  //
+  // Load the Linux kernel from a device path
+  //
+
+  // Try to put the kernel at the start of RAM so as to give it access to all 
memory.
+  // If that fails fall back to try loading it within LINUX_KERNEL_MAX_OFFSET 
of memory start.
+  LinuxImage = PcdGet32(PcdSystemMemoryBase) + 0x80000;
+  Status = BdsLoadImage (LinuxKernelDevicePath, AllocateAddress, &LinuxImage, 
&LinuxImageSize);
+  if (EFI_ERROR(Status)) {
+    // Try again but give the loader more freedom of where to put the image.
+    LinuxImage = LINUX_KERNEL_MAX_OFFSET;
+    Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, 
&LinuxImage, &LinuxImageSize);
+    if (EFI_ERROR(Status)) {
+      Print (L"ERROR: Did not find Linux kernel.\n");
+      return Status;
+    }
+  }
+  // Adjust the kernel location slightly if required. The kernel needs to be 
placed at start
+  //  of memory (2MB aligned) + 0x80000.
+  if ((LinuxImage & LINUX_ALIGN_MASK) != LINUX_ALIGN_VAL) {
+    LinuxImage = (EFI_PHYSICAL_ADDRESS)CopyMem (ALIGN_2MB(LinuxImage) + 
0x80000, (VOID*)(UINTN)LinuxImage, LinuxImageSize);
+  }
+
+  if (InitrdDevicePath) {
+    InitrdImageBase = LINUX_KERNEL_MAX_OFFSET;
+    Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, 
&InitrdImageBase, &InitrdImageBaseSize);
+    if (Status == EFI_OUT_OF_RESOURCES) {
+      Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, 
&InitrdImageBase, &InitrdImageBaseSize);
+    }
+    if (EFI_ERROR (Status)) {
+      Print (L"ERROR: Did not find initrd image.\n");
+      goto EXIT_FREE_LINUX;
+    }
+
+    // Check if the initrd is a uInitrd
+    if (*(UINTN*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) {
+      // Skip the 64-byte image header
+      InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64);
+      InitrdImageSize = InitrdImageBaseSize - 64;
+    } else {
+      InitrdImage = InitrdImageBase;
+      InitrdImageSize = InitrdImageBaseSize;
+    }
+  }
+
+  // Load the FDT binary from a device path.
+  // The FDT will be reloaded later to a more appropriate location for the 
Linux kernel.
+  FdtBlobBase = LINUX_KERNEL_MAX_OFFSET;
+  Status = BdsLoadImage (FdtDevicePath, AllocateMaxAddress, &FdtBlobBase, 
&FdtBlobSize);
+  if (EFI_ERROR(Status)) {
+    Print (L"ERROR: Did not find Device Tree blob.\n");
+    goto EXIT_FREE_INITRD;
+  }
+
+  //
+  // Install secondary core pens if the Power State Coordination Interface is 
not supported
+  //
+  if (FeaturePcdGet (PcdArmPsciSupport) == FALSE) {
+    // Place Pen at the start of Linux memory. We can then tell Linux to not 
use this bit of memory
+    PenBase  = LinuxImage - 0x80000;
+    PenSize  = (UINTN)&SecondariesPenEnd - (UINTN)&SecondariesPenStart;
+
+    // Reserve the memory as RuntimeServices
+    PenBaseStatus = gBS->AllocatePages (AllocateAddress, 
EfiRuntimeServicesCode, EFI_SIZE_TO_PAGES (PenSize), &PenBase);
+    if (EFI_ERROR (PenBaseStatus)) {
+      Print (L"Warning: Failed to reserve the memory required for the 
secondary cores at 0x%lX, Status = %r\n", PenBase, PenBaseStatus);
+      // Even if there is a risk of memory corruption we carry on
+    }
+
+    // Put mailboxes below the pen code so we know where they are relative to 
code.
+    MailBoxBase = (UINTN)PenBase + ((UINTN)&SecondariesPenEnd - 
(UINTN)&SecondariesPenStart);
+    // Make sure this is 8 byte aligned.
+    if (MailBoxBase % sizeof(MailBoxBase) != 0) {
+      MailBoxBase += sizeof(MailBoxBase) - MailBoxBase % sizeof(MailBoxBase);
+    }
+
+    CopyMem ( (VOID*)(PenBase), (VOID*)&SecondariesPenStart, PenSize);
+
+    // Update the MailboxBase variable used in the pen code
+    *(UINTN*)(PenBase + ((UINTN)&AsmMailboxbase - 
(UINTN)&SecondariesPenStart)) = MailBoxBase;
+
+    // Flush caches to make sure our pen gets to mem before we free the cores.
+    ArmCleanDataCache();
+
+    for (Index=0; Index < gST->NumberOfTableEntries; Index++) {
+      // Check for correct GUID type
+      if (CompareGuid (&gArmMpCoreInfoGuid, 
&(gST->ConfigurationTable[Index].VendorGuid))) {
+        UINTN i;
+
+        // Get them under our control. Move from depending on 32bit 
reg(sys_flags) and SWI
+        // to 64 bit addr and WFE
+        ArmProcessorTable = (ARM_PROCESSOR_TABLE 
*)gST->ConfigurationTable[Index].VendorTable;
+        ArmCoreInfoTable = ArmProcessorTable->ArmCpus;
+
+        for (i = 0; i < ArmProcessorTable->NumberOfEntries; i++ ) {
+          // This goes into the SYSFLAGS register for the VE platform. We only 
have one 32bit reg to use
+          MmioWrite32(ArmCoreInfoTable[i].MailboxSetAddress, (UINTN)PenBase);
+
+          // So FDT can set the mailboxes correctly with the parser. These are 
64bit Memory locations.
+          ArmCoreInfoTable[i].MailboxSetAddress = (UINTN)MailBoxBase + 
i*sizeof(MailBoxBase);
+
+          // Clear the mailboxes for the respective cores
+          *((UINTN*)(ArmCoreInfoTable[i].MailboxSetAddress)) = 0x0;
+        }
+      }
+    }
+  }
+
+  // By setting address=0 we leave the memory allocation to the function
+  Status = PrepareFdt (Arguments, InitrdImage, InitrdImageSize, &FdtBlobBase, 
&FdtBlobSize);
+  if (EFI_ERROR(Status)) {
+    Print(L"ERROR: Can not load Linux kernel with Device Tree. Status=0x%X\n", 
Status);
+    goto EXIT_FREE_FDT;
+  }
+
+  return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, 
FdtMachineType);
+
+EXIT_FREE_FDT:
+  if (!EFI_ERROR (PenBaseStatus)) {
+    gBS->FreePages (PenBase, EFI_SIZE_TO_PAGES (PenSize));
+  }
+
+  gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize));
+
+EXIT_FREE_INITRD:
+  if (InitrdDevicePath) {
+    gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));
+  }
+
+EXIT_FREE_LINUX:
+  gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));
+
+  return Status;
+}

Added: trunk/edk2/ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoaderHelper.S
===================================================================
--- trunk/edk2/ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoaderHelper.S             
                (rev 0)
+++ trunk/edk2/ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoaderHelper.S     
2013-07-18 18:13:02 UTC (rev 14487)
@@ -0,0 +1,58 @@
+//
+//  Copyright (c) 2011-2013, ARM Limited. All rights reserved.
+//
+//  This program and the accompanying materials
+//  are licensed and made available under the terms and conditions of the BSD 
License
+//  which accompanies this distribution.  The full text of the license may be 
found at
+//  http://opensource.org/licenses/bsd-license.php
+//
+//  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR 
IMPLIED.
+//
+//
+
+
+/* Secondary core pens for AArch64 Linux booting.
+
+   This code it placed in Linux kernel memory and marked reserved. UEFI ensures
+   that the secondary cores get to this pen and the kernel can then start the
+   cores from here.
+   NOTE: This code must be self-contained.
+*/
+
+#include <Library/ArmLib.h>
+
+.text
+.align 3
+
+ASM_GLOBAL ASM_PFX(SecondariesPenStart)
+ASM_GLOBAL SecondariesPenEnd
+
+ASM_PFX(SecondariesPenStart):
+   // Registers x0-x3 are reserved for future use and should be set to zero.
+   mov x0, xzr
+   mov x1, xzr
+   mov x2, xzr
+   mov x3, xzr
+
+   // Get core position. Taken from ArmPlatformGetCorePosition().
+   // Assumes max 4 cores per cluster.
+   mrs x4, mpidr_el1             // Get MPCore register.
+   and x5, x4, #ARM_CORE_MASK    // Get core number.
+   and x4, x4, #ARM_CLUSTER_MASK // Get cluster number.
+   add x4, x5, x4, LSR #6        // Add scaled cluster number to core number.
+   lsl x4, x4, 3            // Get mailbox offset for this core.
+   ldr x5, AsmMailboxbase   // Get mailbox addr relative to pc (36 bytes 
ahead).
+   add x4, x4, x5           // Add core mailbox offset to base of mailbox.
+1: ldr x5, [x4]             // Load value from mailbox.
+   cmp xzr, x5              // Has the mailbox been set?
+   b.ne 2f                  // If so break out of loop.
+   wfe                      // If not, wait a bit.
+   b 1b                     // Wait over, check if mailbox set again.
+2: br x5                    // Jump to mailbox value.
+
+.align 3 // Make sure the variable below is 8 byte aligned.
+                .global     AsmMailboxbase
+AsmMailboxbase: .xword      0xdeaddeadbeefbeef
+
+SecondariesPenEnd:

Modified: trunk/edk2/ArmPkg/Library/BdsLib/BdsLib.inf
===================================================================
--- trunk/edk2/ArmPkg/Library/BdsLib/BdsLib.inf 2013-07-18 18:07:46 UTC (rev 
14486)
+++ trunk/edk2/ArmPkg/Library/BdsLib/BdsLib.inf 2013-07-18 18:13:02 UTC (rev 
14487)
@@ -31,6 +31,10 @@
   Arm/BdsLinuxLoader.c
   Arm/BdsLinuxAtag.c
 
+[Sources.AARCH64]
+  AArch64/BdsLinuxLoader.c
+  AArch64/BdsLinuxLoaderHelper.S  | GCC
+
 [Packages]
   MdePkg/MdePkg.dec
   EmbeddedPkg/EmbeddedPkg.dec
@@ -49,6 +53,9 @@
   FdtLib
   TimerLib
 
+[LibraryClasses.AARCH64]
+  ArmGicLib
+
 [Guids]
   gEfiFileInfoGuid
   gArmMpCoreInfoGuid
@@ -82,5 +89,9 @@
 [FixedPcd.ARM]
   gArmTokenSpaceGuid.PcdArmLinuxAtagMaxOffset
 
+[FixedPcd.AARCH64]
+  gArmTokenSpaceGuid.PcdGicDistributorBase
+  gArmTokenSpaceGuid.PcdGicSgiIntId
+
 [Depex]
   TRUE

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics
Get end-to-end visibility with application monitoring from AppDynamics
Isolate bottlenecks and diagnose root cause in seconds.
Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk
_______________________________________________
edk2-commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-commits

Reply via email to