summaryrefslogtreecommitdiff
path: root/ArmPlatformPkg/Sec/Sec.c
blob: 4199f381ba9481d0249f1d4a0370901a7d1bad3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/** @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/PrintLib.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>

#define SerialPrint(txt)  SerialPortWrite (txt, AsciiStrLen(txt)+1);

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
  )
{
  CHAR8           Buffer[100];
  UINTN           CharCount;

  // 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
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"UEFI firmware built at %a on %a\n\r",__TIME__, __DATE__);
    SerialPortWrite ((UINT8 *) Buffer, CharCount);

    // 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){
      SerialPrint ("Trust Zone Configuration is disabled\n\r");
    }

    //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(PcdNormalFdBaseAddress);

      // Patch the DRAM to make an infinite loop at the start address
      *StartAddress = 0xEAFFFFFE; // opcode for while(1)

      CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Waiting for firmware at 0x%08X ...\n\r",StartAddress);
      SerialPortWrite ((UINT8 *) Buffer, CharCount);

      // To enter into Non Secure state, we need to make a return from exception
      return_from_exception(PcdGet32(PcdNormalFdBaseAddress));
    } 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 {
    // To enter into Non Secure state, we need to make a return from exception
    return_from_exception(PcdGet32(PcdNormalFdBaseAddress));
  }
  //-------------------- 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(PcdNormalFdBaseAddress);

  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 (
  IN UINT32 Entry,
  IN UINT32 LR
  )
{
  CHAR8           Buffer[100];
  UINTN           CharCount;

  switch (Entry) {
  case 0:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Reset Exception at 0x%X\n\r",LR);
    break;
  case 1:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Undefined Exception at 0x%X\n\r",LR);
    break;
  case 2:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"SWI Exception at 0x%X\n\r",LR);
    break;
  case 3:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"PrefetchAbort Exception at 0x%X\n\r",LR);
    break;
  case 4:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"DataAbort Exception at 0x%X\n\r",LR);
    break;
  case 5:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Reserved Exception at 0x%X\n\r",LR);
    break;
  case 6:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"IRQ Exception at 0x%X\n\r",LR);
    break;
  case 7:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"FIQ Exception at 0x%X\n\r",LR);
    break;
  default:
    CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Unknown Exception at 0x%X\n\r",LR);
    break;
  }
  SerialPortWrite ((UINT8 *) Buffer, CharCount);
  while(1);
}