1 | /** @file
|
---|
2 |
|
---|
3 | Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
|
---|
4 | Copyright (c) 2011, ARM Limited. All rights reserved.
|
---|
5 |
|
---|
6 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
7 |
|
---|
8 | **/
|
---|
9 |
|
---|
10 | #include "CpuDxe.h"
|
---|
11 |
|
---|
12 | #include <Guid/IdleLoopEvent.h>
|
---|
13 |
|
---|
14 | #include <Library/MemoryAllocationLib.h>
|
---|
15 |
|
---|
16 | BOOLEAN mIsFlushingGCD;
|
---|
17 |
|
---|
18 | /**
|
---|
19 | This function flushes the range of addresses from Start to Start+Length
|
---|
20 | from the processor's data cache. If Start is not aligned to a cache line
|
---|
21 | boundary, then the bytes before Start to the preceding cache line boundary
|
---|
22 | are also flushed. If Start+Length is not aligned to a cache line boundary,
|
---|
23 | then the bytes past Start+Length to the end of the next cache line boundary
|
---|
24 | are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
|
---|
25 | supported. If the data cache is fully coherent with all DMA operations, then
|
---|
26 | this function can just return EFI_SUCCESS. If the processor does not support
|
---|
27 | flushing a range of the data cache, then the entire data cache can be flushed.
|
---|
28 |
|
---|
29 | @param This The EFI_CPU_ARCH_PROTOCOL instance.
|
---|
30 | @param Start The beginning physical address to flush from the processor's data
|
---|
31 | cache.
|
---|
32 | @param Length The number of bytes to flush from the processor's data cache. This
|
---|
33 | function may flush more bytes than Length specifies depending upon
|
---|
34 | the granularity of the flush operation that the processor supports.
|
---|
35 | @param FlushType Specifies the type of flush operation to perform.
|
---|
36 |
|
---|
37 | @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from
|
---|
38 | the processor's data cache.
|
---|
39 | @retval EFI_UNSUPPORTED The processor does not support the cache flush type specified
|
---|
40 | by FlushType.
|
---|
41 | @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed
|
---|
42 | from the processor's data cache.
|
---|
43 |
|
---|
44 | **/
|
---|
45 | EFI_STATUS
|
---|
46 | EFIAPI
|
---|
47 | CpuFlushCpuDataCache (
|
---|
48 | IN EFI_CPU_ARCH_PROTOCOL *This,
|
---|
49 | IN EFI_PHYSICAL_ADDRESS Start,
|
---|
50 | IN UINT64 Length,
|
---|
51 | IN EFI_CPU_FLUSH_TYPE FlushType
|
---|
52 | )
|
---|
53 | {
|
---|
54 | switch (FlushType) {
|
---|
55 | case EfiCpuFlushTypeWriteBack:
|
---|
56 | WriteBackDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
|
---|
57 | break;
|
---|
58 | case EfiCpuFlushTypeInvalidate:
|
---|
59 | InvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
|
---|
60 | break;
|
---|
61 | case EfiCpuFlushTypeWriteBackInvalidate:
|
---|
62 | WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
|
---|
63 | break;
|
---|
64 | default:
|
---|
65 | return EFI_INVALID_PARAMETER;
|
---|
66 | }
|
---|
67 |
|
---|
68 | return EFI_SUCCESS;
|
---|
69 | }
|
---|
70 |
|
---|
71 | /**
|
---|
72 | This function enables interrupt processing by the processor.
|
---|
73 |
|
---|
74 | @param This The EFI_CPU_ARCH_PROTOCOL instance.
|
---|
75 |
|
---|
76 | @retval EFI_SUCCESS Interrupts are enabled on the processor.
|
---|
77 | @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor.
|
---|
78 |
|
---|
79 | **/
|
---|
80 | EFI_STATUS
|
---|
81 | EFIAPI
|
---|
82 | CpuEnableInterrupt (
|
---|
83 | IN EFI_CPU_ARCH_PROTOCOL *This
|
---|
84 | )
|
---|
85 | {
|
---|
86 | ArmEnableInterrupts ();
|
---|
87 |
|
---|
88 | return EFI_SUCCESS;
|
---|
89 | }
|
---|
90 |
|
---|
91 | /**
|
---|
92 | This function disables interrupt processing by the processor.
|
---|
93 |
|
---|
94 | @param This The EFI_CPU_ARCH_PROTOCOL instance.
|
---|
95 |
|
---|
96 | @retval EFI_SUCCESS Interrupts are disabled on the processor.
|
---|
97 | @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor.
|
---|
98 |
|
---|
99 | **/
|
---|
100 | EFI_STATUS
|
---|
101 | EFIAPI
|
---|
102 | CpuDisableInterrupt (
|
---|
103 | IN EFI_CPU_ARCH_PROTOCOL *This
|
---|
104 | )
|
---|
105 | {
|
---|
106 | ArmDisableInterrupts ();
|
---|
107 |
|
---|
108 | return EFI_SUCCESS;
|
---|
109 | }
|
---|
110 |
|
---|
111 | /**
|
---|
112 | This function retrieves the processor's current interrupt state a returns it in
|
---|
113 | State. If interrupts are currently enabled, then TRUE is returned. If interrupts
|
---|
114 | are currently disabled, then FALSE is returned.
|
---|
115 |
|
---|
116 | @param This The EFI_CPU_ARCH_PROTOCOL instance.
|
---|
117 | @param State A pointer to the processor's current interrupt state. Set to TRUE if
|
---|
118 | interrupts are enabled and FALSE if interrupts are disabled.
|
---|
119 |
|
---|
120 | @retval EFI_SUCCESS The processor's current interrupt state was returned in State.
|
---|
121 | @retval EFI_INVALID_PARAMETER State is NULL.
|
---|
122 |
|
---|
123 | **/
|
---|
124 | EFI_STATUS
|
---|
125 | EFIAPI
|
---|
126 | CpuGetInterruptState (
|
---|
127 | IN EFI_CPU_ARCH_PROTOCOL *This,
|
---|
128 | OUT BOOLEAN *State
|
---|
129 | )
|
---|
130 | {
|
---|
131 | if (State == NULL) {
|
---|
132 | return EFI_INVALID_PARAMETER;
|
---|
133 | }
|
---|
134 |
|
---|
135 | *State = ArmGetInterruptState ();
|
---|
136 | return EFI_SUCCESS;
|
---|
137 | }
|
---|
138 |
|
---|
139 | /**
|
---|
140 | This function generates an INIT on the processor. If this function succeeds, then the
|
---|
141 | processor will be reset, and control will not be returned to the caller. If InitType is
|
---|
142 | not supported by this processor, or the processor cannot programmatically generate an
|
---|
143 | INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
|
---|
144 | occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
|
---|
145 |
|
---|
146 | @param This The EFI_CPU_ARCH_PROTOCOL instance.
|
---|
147 | @param InitType The type of processor INIT to perform.
|
---|
148 |
|
---|
149 | @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen.
|
---|
150 | @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported
|
---|
151 | by this processor.
|
---|
152 | @retval EFI_DEVICE_ERROR The processor INIT failed.
|
---|
153 |
|
---|
154 | **/
|
---|
155 | EFI_STATUS
|
---|
156 | EFIAPI
|
---|
157 | CpuInit (
|
---|
158 | IN EFI_CPU_ARCH_PROTOCOL *This,
|
---|
159 | IN EFI_CPU_INIT_TYPE InitType
|
---|
160 | )
|
---|
161 | {
|
---|
162 | return EFI_UNSUPPORTED;
|
---|
163 | }
|
---|
164 |
|
---|
165 | EFI_STATUS
|
---|
166 | EFIAPI
|
---|
167 | CpuRegisterInterruptHandler (
|
---|
168 | IN EFI_CPU_ARCH_PROTOCOL *This,
|
---|
169 | IN EFI_EXCEPTION_TYPE InterruptType,
|
---|
170 | IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
|
---|
171 | )
|
---|
172 | {
|
---|
173 | return RegisterInterruptHandler (InterruptType, InterruptHandler);
|
---|
174 | }
|
---|
175 |
|
---|
176 | EFI_STATUS
|
---|
177 | EFIAPI
|
---|
178 | CpuGetTimerValue (
|
---|
179 | IN EFI_CPU_ARCH_PROTOCOL *This,
|
---|
180 | IN UINT32 TimerIndex,
|
---|
181 | OUT UINT64 *TimerValue,
|
---|
182 | OUT UINT64 *TimerPeriod OPTIONAL
|
---|
183 | )
|
---|
184 | {
|
---|
185 | return EFI_UNSUPPORTED;
|
---|
186 | }
|
---|
187 |
|
---|
188 | /**
|
---|
189 | Callback function for idle events.
|
---|
190 |
|
---|
191 | @param Event Event whose notification function is being invoked.
|
---|
192 | @param Context The pointer to the notification function's context,
|
---|
193 | which is implementation-dependent.
|
---|
194 |
|
---|
195 | **/
|
---|
196 | VOID
|
---|
197 | EFIAPI
|
---|
198 | IdleLoopEventCallback (
|
---|
199 | IN EFI_EVENT Event,
|
---|
200 | IN VOID *Context
|
---|
201 | )
|
---|
202 | {
|
---|
203 | CpuSleep ();
|
---|
204 | }
|
---|
205 |
|
---|
206 | //
|
---|
207 | // Globals used to initialize the protocol
|
---|
208 | //
|
---|
209 | EFI_HANDLE mCpuHandle = NULL;
|
---|
210 | EFI_CPU_ARCH_PROTOCOL mCpu = {
|
---|
211 | CpuFlushCpuDataCache,
|
---|
212 | CpuEnableInterrupt,
|
---|
213 | CpuDisableInterrupt,
|
---|
214 | CpuGetInterruptState,
|
---|
215 | CpuInit,
|
---|
216 | CpuRegisterInterruptHandler,
|
---|
217 | CpuGetTimerValue,
|
---|
218 | CpuSetMemoryAttributes,
|
---|
219 | 0, // NumberOfTimers
|
---|
220 | 2048, // DmaBufferAlignment
|
---|
221 | };
|
---|
222 |
|
---|
223 | STATIC
|
---|
224 | VOID
|
---|
225 | InitializeDma (
|
---|
226 | IN OUT EFI_CPU_ARCH_PROTOCOL *CpuArchProtocol
|
---|
227 | )
|
---|
228 | {
|
---|
229 | CpuArchProtocol->DmaBufferAlignment = ArmCacheWritebackGranule ();
|
---|
230 | }
|
---|
231 |
|
---|
232 | /**
|
---|
233 | Map all EfiConventionalMemory regions in the memory map with NX
|
---|
234 | attributes so that allocating or freeing EfiBootServicesData regions
|
---|
235 | does not result in changes to memory permission attributes.
|
---|
236 |
|
---|
237 | **/
|
---|
238 | STATIC
|
---|
239 | VOID
|
---|
240 | RemapUnusedMemoryNx (
|
---|
241 | VOID
|
---|
242 | )
|
---|
243 | {
|
---|
244 | UINT64 TestBit;
|
---|
245 | UINTN MemoryMapSize;
|
---|
246 | UINTN MapKey;
|
---|
247 | UINTN DescriptorSize;
|
---|
248 | UINT32 DescriptorVersion;
|
---|
249 | EFI_MEMORY_DESCRIPTOR *MemoryMap;
|
---|
250 | EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
|
---|
251 | EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
|
---|
252 | EFI_STATUS Status;
|
---|
253 |
|
---|
254 | TestBit = LShiftU64 (1, EfiBootServicesData);
|
---|
255 | if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) == 0) {
|
---|
256 | return;
|
---|
257 | }
|
---|
258 |
|
---|
259 | MemoryMapSize = 0;
|
---|
260 | MemoryMap = NULL;
|
---|
261 |
|
---|
262 | Status = gBS->GetMemoryMap (
|
---|
263 | &MemoryMapSize,
|
---|
264 | MemoryMap,
|
---|
265 | &MapKey,
|
---|
266 | &DescriptorSize,
|
---|
267 | &DescriptorVersion
|
---|
268 | );
|
---|
269 | ASSERT (Status == EFI_BUFFER_TOO_SMALL);
|
---|
270 | do {
|
---|
271 | MemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocatePool (MemoryMapSize);
|
---|
272 | ASSERT (MemoryMap != NULL);
|
---|
273 | Status = gBS->GetMemoryMap (
|
---|
274 | &MemoryMapSize,
|
---|
275 | MemoryMap,
|
---|
276 | &MapKey,
|
---|
277 | &DescriptorSize,
|
---|
278 | &DescriptorVersion
|
---|
279 | );
|
---|
280 | if (EFI_ERROR (Status)) {
|
---|
281 | FreePool (MemoryMap);
|
---|
282 | }
|
---|
283 | } while (Status == EFI_BUFFER_TOO_SMALL);
|
---|
284 |
|
---|
285 | ASSERT_EFI_ERROR (Status);
|
---|
286 |
|
---|
287 | MemoryMapEntry = MemoryMap;
|
---|
288 | MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
|
---|
289 | while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
|
---|
290 | if (MemoryMapEntry->Type == EfiConventionalMemory) {
|
---|
291 | ArmSetMemoryAttributes (
|
---|
292 | MemoryMapEntry->PhysicalStart,
|
---|
293 | EFI_PAGES_TO_SIZE (MemoryMapEntry->NumberOfPages),
|
---|
294 | EFI_MEMORY_XP,
|
---|
295 | EFI_MEMORY_XP
|
---|
296 | );
|
---|
297 | }
|
---|
298 |
|
---|
299 | MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
---|
300 | }
|
---|
301 | }
|
---|
302 |
|
---|
303 | EFI_STATUS
|
---|
304 | CpuDxeInitialize (
|
---|
305 | IN EFI_HANDLE ImageHandle,
|
---|
306 | IN EFI_SYSTEM_TABLE *SystemTable
|
---|
307 | )
|
---|
308 | {
|
---|
309 | EFI_STATUS Status;
|
---|
310 | EFI_EVENT IdleLoopEvent;
|
---|
311 |
|
---|
312 | InitializeExceptions (&mCpu);
|
---|
313 |
|
---|
314 | InitializeDma (&mCpu);
|
---|
315 |
|
---|
316 | //
|
---|
317 | // Once we install the CPU arch protocol, the DXE core's memory
|
---|
318 | // protection routines will invoke them to manage the permissions of page
|
---|
319 | // allocations as they are created. Given that this includes pages
|
---|
320 | // allocated for page tables by this driver, we must ensure that unused
|
---|
321 | // memory is mapped with the same permissions as boot services data
|
---|
322 | // regions. Otherwise, we may end up with unbounded recursion, due to the
|
---|
323 | // fact that updating permissions on a newly allocated page table may trigger
|
---|
324 | // a block entry split, which triggers a page table allocation, etc etc
|
---|
325 | //
|
---|
326 | if (FeaturePcdGet (PcdRemapUnusedMemoryNx)) {
|
---|
327 | RemapUnusedMemoryNx ();
|
---|
328 | }
|
---|
329 |
|
---|
330 | Status = gBS->InstallMultipleProtocolInterfaces (
|
---|
331 | &mCpuHandle,
|
---|
332 | &gEfiCpuArchProtocolGuid,
|
---|
333 | &mCpu,
|
---|
334 | &gEfiMemoryAttributeProtocolGuid,
|
---|
335 | &mMemoryAttribute,
|
---|
336 | NULL
|
---|
337 | );
|
---|
338 |
|
---|
339 | //
|
---|
340 | // Make sure GCD and MMU settings match. This API calls gDS->SetMemorySpaceAttributes ()
|
---|
341 | // and that calls EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes, so this code needs to go
|
---|
342 | // after the protocol is installed
|
---|
343 | //
|
---|
344 | mIsFlushingGCD = TRUE;
|
---|
345 | SyncCacheConfig (&mCpu);
|
---|
346 | mIsFlushingGCD = FALSE;
|
---|
347 |
|
---|
348 | //
|
---|
349 | // Setup a callback for idle events
|
---|
350 | //
|
---|
351 | Status = gBS->CreateEventEx (
|
---|
352 | EVT_NOTIFY_SIGNAL,
|
---|
353 | TPL_NOTIFY,
|
---|
354 | IdleLoopEventCallback,
|
---|
355 | NULL,
|
---|
356 | &gIdleLoopEventGuid,
|
---|
357 | &IdleLoopEvent
|
---|
358 | );
|
---|
359 | ASSERT_EFI_ERROR (Status);
|
---|
360 |
|
---|
361 | return Status;
|
---|
362 | }
|
---|