diff options
Diffstat (limited to 'ArmPlatformPkg/Sec/Sec.c')
-rw-r--r-- | ArmPlatformPkg/Sec/Sec.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/ArmPlatformPkg/Sec/Sec.c b/ArmPlatformPkg/Sec/Sec.c new file mode 100644 index 0000000000..2ae01d8e54 --- /dev/null +++ b/ArmPlatformPkg/Sec/Sec.c @@ -0,0 +1,275 @@ +/** @file +* Main file supporting the SEC Phase for Versatile Express +* +* Copyright (c) 2011, 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/DebugLib.h> +#include <Library/PcdLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/ArmLib.h> +#include <Chipset/ArmV7.h> +#include <Drivers/PL390Gic.h> +#include <Library/L2X0CacheLib.h> +#include <Library/SerialPortLib.h> +#include <Library/ArmPlatformLib.h> + +extern VOID *monitor_vector_table; + +VOID ArmSetupGicNonSecure ( + IN INTN GicDistributorBase, + IN INTN GicInterruptInterfaceBase +); + +// Vector Table for Sec Phase +VOID SecVectorTable (VOID); + +VOID NonSecureWaitForFirmware ( + VOID + ); + +VOID +enter_monitor_mode( + IN VOID* Stack + ); + +VOID +return_from_exception ( + IN UINTN NonSecureBase + ); + +VOID +copy_cpsr_into_spsr ( + VOID + ); + +VOID +CEntryPoint ( + IN UINTN CoreId + ) +{ + // Primary CPU clears out the SCU tag RAMs, secondaries wait + if (CoreId == 0) { + if (FixedPcdGet32(PcdMPCoreSupport)) { + ArmInvalidScu(); + } + + // SEC phase needs to run library constructors by hand. This assumes we are linked against the SerialLib + // In non SEC modules the init call is in autogenerated code. + SerialPortInitialize (); + // Start talking + DEBUG ((EFI_D_ERROR, "UART Enabled\n")); + + // Now we've got UART, make the check: + // - The Vector table must be 32-byte aligned + ASSERT(((UINT32)SecVectorTable & ((1 << 5)-1)) == 0); + } + + // Invalidate the data cache. Doesn't have to do the Data cache clean. + ArmInvalidateDataCache(); + + //Invalidate Instruction Cache + ArmInvalidateInstructionCache(); + + //Invalidate I & D TLBs + ArmInvalidateInstructionAndDataTlb(); + + // Enable Full Access to CoProcessors + ArmWriteCPACR (CPACR_CP_FULL_ACCESS); + + // Enable SWP instructions + ArmEnableSWPInstruction(); + + // Enable program flow prediction, if supported. + ArmEnableBranchPrediction(); + + if (FixedPcdGet32(PcdVFPEnabled)) { + ArmEnableVFP(); + } + + if (CoreId == 0) { + // Initialize L2X0 but not enabled + L2x0CacheInit(PcdGet32(PcdL2x0ControllerBase), FALSE); + + // If we skip the PEI Core we could want to initialize the DRAM in the SEC phase. + // If we are in standalone, we need the initialization to copy the UEFI firmware into DRAM + if (FeaturePcdGet(PcdSkipPeiCore) || !FeaturePcdGet(PcdStandalone)) { + // Initialize system memory (DRAM) + ArmPlatformInitializeSystemMemory(); + } + + // Turn Off NOR flash remapping to 0. We can will now see DRAM in low memory + ArmPlatformBootRemapping(); + } + + // Test if Trustzone is supported on this platform + if (ArmPlatformTrustzoneSupported()) { + if (FixedPcdGet32(PcdMPCoreSupport)) { + // Setup SMP in Non Secure world + ArmSetupSmpNonSecure(CoreId); + } + + // Enter Monitor Mode + enter_monitor_mode((VOID*)(PcdGet32(PcdCPUCoresSecMonStackBase) + (PcdGet32(PcdCPUCoreSecMonStackSize) * CoreId))); + + //Write the monitor mode vector table address + ArmWriteVMBar((UINT32) &monitor_vector_table); + + //-------------------- Monitor Mode --------------------- + // setup the Trustzone Chipsets + if (CoreId == 0) { + ArmPlatformTrustzoneInit(); + + // Wake up the secondary cores by sending a interrupt to everyone else + // NOTE 1: The Software Generated Interrupts are always enabled on Cortex-A9 + // MPcore test chip on Versatile Express board, So the Software doesn't have to + // enable SGI's explicitly. + // 2: As no other Interrupts are enabled, doesn't have to worry about the priority. + // 3: As all the cores are in secure state, use secure SGI's + // + + PL390GicEnableDistributor (PcdGet32(PcdGicDistributorBase)); + PL390GicEnableInterruptInterface(PcdGet32(PcdGicInterruptInterfaceBase)); + + // Send SGI to all Secondary core to wake them up from WFI state. + PL390GicSendSgiTo (PcdGet32(PcdGicDistributorBase), GIC_ICDSGIR_FILTER_EVERYONEELSE, 0x0E); + } else { + // The secondary cores need to wait until the Trustzone chipsets configuration is done + // before swtching to Non Secure World + + // Enabled GIC CPU Interface + PL390GicEnableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase)); + + // Waiting for the SGI from the primary core + ArmCallWFI(); + + //Acknowledge the interrupt and send End of Interrupt signal. + PL390GicAcknowledgeSgiFrom(PcdGet32(PcdGicInterruptInterfaceBase),0/*CoreId*/); + } + + // Transfer the interrupt to Non-secure World + PL390GicSetupNonSecure(PcdGet32(PcdGicDistributorBase),PcdGet32(PcdGicInterruptInterfaceBase)); + + // Write to CP15 Non-secure Access Control Register : + // - Enable CP10 and CP11 accesses in NS World + // - Enable Access to Preload Engine in NS World + // - Enable lockable TLB entries allocation in NS world + // - Enable R/W access to SMP bit of Auxiliary Control Register in NS world + ArmWriteNsacr(NSACR_NS_SMP | NSACR_TL | NSACR_PLE | NSACR_CP(10) | NSACR_CP(11)); + + // CP15 Secure Configuration Register with Non Secure bit (SCR_NS), CPSR.A modified in any + // security state (SCR_AW), CPSR.F modified in any security state (SCR_FW) + ArmWriteScr(SCR_NS | SCR_FW | SCR_AW); + } else { + if(0 == CoreId){ + DEBUG ((EFI_D_ERROR, "Trust Zone Configuration is disabled\n")); + } + + //Trustzone is not enabled, just enable the Distributor and CPU interface + PL390GicEnableInterruptInterface(PcdGet32(PcdGicInterruptInterfaceBase)); + + // With Trustzone support the transition from Sec to Normal world is done by return_from_exception(). + // If we want to keep this function call we need to ensure the SVC's SPSR point to the same Program + // Status Register as the the current one (CPSR). + copy_cpsr_into_spsr(); + } + + // If ArmVe has not been built as Standalone then we need to patch the DRAM to add an infinite loop at the start address + if (FeaturePcdGet(PcdStandalone) == FALSE) { + if (CoreId == 0) { + UINTN* StartAddress = (UINTN*)PcdGet32(PcdEmbeddedFdBaseAddress); + + DEBUG ((EFI_D_ERROR, "Waiting for firmware at 0x%08X ...\n",StartAddress)); + + // Patch the DRAM to make an infinite loop at the start address + *StartAddress = 0xEAFFFFFE; // opcode for while(1) + + // To enter into Non Secure state, we need to make a return from exception + return_from_exception(PcdGet32(PcdEmbeddedFdBaseAddress)); + } else { + // When the primary core is stopped by the hardware debugger to copy the firmware + // into DRAM. The secondary cores are still running. As soon as the first bytes of + // the firmware are written into DRAM, the secondary cores will start to execute the + // code even if the firmware is not entirely written into the memory. + // That's why the secondary cores need to be parked in WFI and wake up once the + // firmware is ready. + + // Enter Secondary Cores into non Secure State. To enter into Non Secure state, we need to make a return from exception + return_from_exception((UINTN)NonSecureWaitForFirmware); + } + } else { + if (CoreId == 0) { + DEBUG ((EFI_D_ERROR, "Standalone Firmware\n")); + } + + // To enter into Non Secure state, we need to make a return from exception + return_from_exception(PcdGet32(PcdEmbeddedFdBaseAddress)); + } + //-------------------- Non Secure Mode --------------------- + + // PEI Core should always load and never return + ASSERT (FALSE); +} + +// When the firmware is built as not Standalone, the secondary cores need to wait the firmware +// entirely written into DRAM. It is the firmware from DRAM which will wake up the secondary cores. +VOID NonSecureWaitForFirmware() { + VOID (*secondary_start)(VOID); + + // The secondary cores will execute the fimrware once wake from WFI. + secondary_start = (VOID (*)())PcdGet32(PcdEmbeddedFdBaseAddress); + + ArmCallWFI(); + + //Acknowledge the interrupt and send End of Interrupt signal. + PL390GicAcknowledgeSgiFrom(PcdGet32(PcdGicInterruptInterfaceBase),0/*CoreId*/); + + //Jump to secondary core entry point. + secondary_start(); + + // PEI Core should always load and never return + ASSERT (FALSE); +} + +VOID SecCommonExceptionEntry(UINT32 Entry, UINT32 LR) { + switch (Entry) { + case 0: + DEBUG((EFI_D_ERROR,"Reset Exception at 0x%X\n",LR)); + break; + case 1: + DEBUG((EFI_D_ERROR,"Undefined Exception at 0x%X\n",LR)); + break; + case 2: + DEBUG((EFI_D_ERROR,"SWI Exception at 0x%X\n",LR)); + break; + case 3: + DEBUG((EFI_D_ERROR,"PrefetchAbort Exception at 0x%X\n",LR)); + break; + case 4: + DEBUG((EFI_D_ERROR,"DataAbort Exception at 0x%X\n",LR)); + break; + case 5: + DEBUG((EFI_D_ERROR,"Reserved Exception at 0x%X\n",LR)); + break; + case 6: + DEBUG((EFI_D_ERROR,"IRQ Exception at 0x%X\n",LR)); + break; + case 7: + DEBUG((EFI_D_ERROR,"FIQ Exception at 0x%X\n",LR)); + break; + default: + DEBUG((EFI_D_ERROR,"Unknown Exception at 0x%X\n",LR)); + break; + } + while(1); +} |