VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/ArmPkg/Drivers/CpuDxe/CpuDxe.c@ 108794

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

  • Property svn:eol-style set to native
File size: 18.8 KB
Line 
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
16BOOLEAN mIsFlushingGCD;
17
18// Shadow state for the CPU interrupt en/disabled bit
19STATIC BOOLEAN mInterruptsEnabled;
20STATIC VOID *mHardwareInterruptProtocolNotifyEventRegistration;
21
22/**
23 Mark interrupts as enabled in the shadow variable but don't actually enable them yet.
24**/
25STATIC
26EFI_STATUS
27EFIAPI
28CpuShadowEnableInterrupt (
29 IN EFI_CPU_ARCH_PROTOCOL *This
30 )
31{
32 mInterruptsEnabled = TRUE;
33
34 return EFI_SUCCESS;
35}
36
37/**
38 Mark interrupts as disabled in the shadow variable.
39**/
40STATIC
41EFI_STATUS
42EFIAPI
43CpuShadowDisableInterrupt (
44 IN EFI_CPU_ARCH_PROTOCOL *This
45 )
46{
47 mInterruptsEnabled = FALSE;
48
49 return EFI_SUCCESS;
50}
51
52/**
53 Return whether interrupts would be enabled based on the shadow variable.
54**/
55STATIC
56EFI_STATUS
57EFIAPI
58CpuShadowGetInterruptState (
59 IN EFI_CPU_ARCH_PROTOCOL *This,
60 OUT BOOLEAN *State
61 )
62{
63 if (State == NULL) {
64 return EFI_INVALID_PARAMETER;
65 }
66
67 *State = mInterruptsEnabled;
68 return EFI_SUCCESS;
69}
70
71/**
72 This function flushes the range of addresses from Start to Start+Length
73 from the processor's data cache. If Start is not aligned to a cache line
74 boundary, then the bytes before Start to the preceding cache line boundary
75 are also flushed. If Start+Length is not aligned to a cache line boundary,
76 then the bytes past Start+Length to the end of the next cache line boundary
77 are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
78 supported. If the data cache is fully coherent with all DMA operations, then
79 this function can just return EFI_SUCCESS. If the processor does not support
80 flushing a range of the data cache, then the entire data cache can be flushed.
81
82 @param This The EFI_CPU_ARCH_PROTOCOL instance.
83 @param Start The beginning physical address to flush from the processor's data
84 cache.
85 @param Length The number of bytes to flush from the processor's data cache. This
86 function may flush more bytes than Length specifies depending upon
87 the granularity of the flush operation that the processor supports.
88 @param FlushType Specifies the type of flush operation to perform.
89
90 @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from
91 the processor's data cache.
92 @retval EFI_UNSUPPORTED The processor does not support the cache flush type specified
93 by FlushType.
94 @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed
95 from the processor's data cache.
96
97**/
98STATIC
99EFI_STATUS
100EFIAPI
101CpuFlushCpuDataCache (
102 IN EFI_CPU_ARCH_PROTOCOL *This,
103 IN EFI_PHYSICAL_ADDRESS Start,
104 IN UINT64 Length,
105 IN EFI_CPU_FLUSH_TYPE FlushType
106 )
107{
108 switch (FlushType) {
109 case EfiCpuFlushTypeWriteBack:
110 WriteBackDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
111 break;
112 case EfiCpuFlushTypeInvalidate:
113 InvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
114 break;
115 case EfiCpuFlushTypeWriteBackInvalidate:
116 WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
117 break;
118 default:
119 return EFI_INVALID_PARAMETER;
120 }
121
122 return EFI_SUCCESS;
123}
124
125/**
126 This function enables interrupt processing by the processor.
127
128 @param This The EFI_CPU_ARCH_PROTOCOL instance.
129
130 @retval EFI_SUCCESS Interrupts are enabled on the processor.
131 @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor.
132
133**/
134STATIC
135EFI_STATUS
136EFIAPI
137CpuEnableInterrupt (
138 IN EFI_CPU_ARCH_PROTOCOL *This
139 )
140{
141 ArmEnableInterrupts ();
142
143 return EFI_SUCCESS;
144}
145
146/**
147 This function disables interrupt processing by the processor.
148
149 @param This The EFI_CPU_ARCH_PROTOCOL instance.
150
151 @retval EFI_SUCCESS Interrupts are disabled on the processor.
152 @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor.
153
154**/
155STATIC
156EFI_STATUS
157EFIAPI
158CpuDisableInterrupt (
159 IN EFI_CPU_ARCH_PROTOCOL *This
160 )
161{
162 ArmDisableInterrupts ();
163
164 return EFI_SUCCESS;
165}
166
167/**
168 This function retrieves the processor's current interrupt state a returns it in
169 State. If interrupts are currently enabled, then TRUE is returned. If interrupts
170 are currently disabled, then FALSE is returned.
171
172 @param This The EFI_CPU_ARCH_PROTOCOL instance.
173 @param State A pointer to the processor's current interrupt state. Set to TRUE if
174 interrupts are enabled and FALSE if interrupts are disabled.
175
176 @retval EFI_SUCCESS The processor's current interrupt state was returned in State.
177 @retval EFI_INVALID_PARAMETER State is NULL.
178
179**/
180STATIC
181EFI_STATUS
182EFIAPI
183CpuGetInterruptState (
184 IN EFI_CPU_ARCH_PROTOCOL *This,
185 OUT BOOLEAN *State
186 )
187{
188 if (State == NULL) {
189 return EFI_INVALID_PARAMETER;
190 }
191
192 *State = ArmGetInterruptState ();
193 return EFI_SUCCESS;
194}
195
196/**
197 This function generates an INIT on the processor. If this function succeeds, then the
198 processor will be reset, and control will not be returned to the caller. If InitType is
199 not supported by this processor, or the processor cannot programmatically generate an
200 INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
201 occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
202
203 @param This The EFI_CPU_ARCH_PROTOCOL instance.
204 @param InitType The type of processor INIT to perform.
205
206 @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen.
207 @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported
208 by this processor.
209 @retval EFI_DEVICE_ERROR The processor INIT failed.
210
211**/
212STATIC
213EFI_STATUS
214EFIAPI
215CpuInit (
216 IN EFI_CPU_ARCH_PROTOCOL *This,
217 IN EFI_CPU_INIT_TYPE InitType
218 )
219{
220 return EFI_UNSUPPORTED;
221}
222
223STATIC
224EFI_STATUS
225EFIAPI
226CpuRegisterInterruptHandler (
227 IN EFI_CPU_ARCH_PROTOCOL *This,
228 IN EFI_EXCEPTION_TYPE InterruptType,
229 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
230 )
231{
232 return RegisterInterruptHandler (InterruptType, InterruptHandler);
233}
234
235STATIC
236EFI_STATUS
237EFIAPI
238CpuGetTimerValue (
239 IN EFI_CPU_ARCH_PROTOCOL *This,
240 IN UINT32 TimerIndex,
241 OUT UINT64 *TimerValue,
242 OUT UINT64 *TimerPeriod OPTIONAL
243 )
244{
245 return EFI_UNSUPPORTED;
246}
247
248/**
249 Callback function for idle events.
250
251 @param Event Event whose notification function is being invoked.
252 @param Context The pointer to the notification function's context,
253 which is implementation-dependent.
254
255**/
256STATIC
257VOID
258EFIAPI
259IdleLoopEventCallback (
260 IN EFI_EVENT Event,
261 IN VOID *Context
262 )
263{
264 CpuSleep ();
265}
266
267//
268// Globals used to initialize the protocol
269//
270STATIC EFI_CPU_ARCH_PROTOCOL mCpu = {
271 CpuFlushCpuDataCache,
272 CpuShadowEnableInterrupt,
273 CpuShadowDisableInterrupt,
274 CpuShadowGetInterruptState,
275 CpuInit,
276 CpuRegisterInterruptHandler,
277 CpuGetTimerValue,
278 CpuSetMemoryAttributes,
279 0, // NumberOfTimers
280 2048, // DmaBufferAlignment
281};
282
283STATIC
284VOID
285InitializeDma (
286 IN OUT EFI_CPU_ARCH_PROTOCOL *CpuArchProtocol
287 )
288{
289 CpuArchProtocol->DmaBufferAlignment = ArmCacheWritebackGranule ();
290}
291
292#ifndef VBOX
293/**
294 Map all EfiConventionalMemory regions in the memory map with NX
295 attributes so that allocating or freeing EfiBootServicesData regions
296 does not result in changes to memory permission attributes.
297
298**/
299STATIC
300VOID
301RemapUnusedMemoryNx (
302 VOID
303 )
304{
305 UINT64 TestBit;
306 UINTN MemoryMapSize;
307 UINTN MapKey;
308 UINTN DescriptorSize;
309 UINT32 DescriptorVersion;
310 EFI_MEMORY_DESCRIPTOR *MemoryMap;
311 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
312 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
313 EFI_STATUS Status;
314
315 TestBit = LShiftU64 (1, EfiBootServicesData);
316 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) == 0) {
317 return;
318 }
319
320 MemoryMapSize = 0;
321 MemoryMap = NULL;
322
323 Status = gBS->GetMemoryMap (
324 &MemoryMapSize,
325 MemoryMap,
326 &MapKey,
327 &DescriptorSize,
328 &DescriptorVersion
329 );
330 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
331 do {
332 MemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocatePool (MemoryMapSize);
333 ASSERT (MemoryMap != NULL);
334 Status = gBS->GetMemoryMap (
335 &MemoryMapSize,
336 MemoryMap,
337 &MapKey,
338 &DescriptorSize,
339 &DescriptorVersion
340 );
341 if (EFI_ERROR (Status)) {
342 FreePool (MemoryMap);
343 }
344 } while (Status == EFI_BUFFER_TOO_SMALL);
345
346 ASSERT_EFI_ERROR (Status);
347
348 MemoryMapEntry = MemoryMap;
349 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
350 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
351 if (MemoryMapEntry->Type == EfiConventionalMemory) {
352 ArmSetMemoryAttributes (
353 MemoryMapEntry->PhysicalStart,
354 EFI_PAGES_TO_SIZE (MemoryMapEntry->NumberOfPages),
355 EFI_MEMORY_XP,
356 EFI_MEMORY_XP
357 );
358 }
359
360 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
361 }
362}
363#endif
364
365STATIC
366VOID
367EFIAPI
368HardwareInterruptProtocolNotify (
369 IN EFI_EVENT Event,
370 IN VOID *Context
371 )
372{
373 VOID *Protocol;
374 EFI_STATUS Status;
375
376 Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL, &Protocol);
377 if (EFI_ERROR (Status)) {
378 return;
379 }
380
381 //
382 // Now that the dedicated driver has taken control of the interrupt
383 // controller, we can allow interrupts to be enabled on the CPU side. So swap
384 // out the function stubs that manipulate the shadow state with the real
385 // ones. Interrupts are still disabled at the CPU so these fields can be set
386 // in any order.
387 //
388 mCpu.EnableInterrupt = CpuEnableInterrupt;
389 mCpu.DisableInterrupt = CpuDisableInterrupt;
390 mCpu.GetInterruptState = CpuGetInterruptState;
391
392 if (mInterruptsEnabled) {
393 ArmEnableInterrupts ();
394 }
395
396 gBS->CloseEvent (Event);
397}
398
399EFI_STATUS
400CpuDxeInitialize (
401 IN EFI_HANDLE ImageHandle,
402 IN EFI_SYSTEM_TABLE *SystemTable
403 )
404{
405 EFI_STATUS Status;
406 EFI_EVENT IdleLoopEvent;
407 EFI_HANDLE CpuHandle;
408
409 ArmDisableInterrupts ();
410 InitializeExceptions ();
411
412 InitializeDma (&mCpu);
413
414#ifdef VBOX
415 /** @todo r=aeichner
416 * Exposing the EFI_MEMORY_ATTRIBUTE protocol (added with edk2-stable202308) breaks OL9 and
417 * Ubuntu 23.04 server guests at least. They use a shima64.efi and grubaa64.efi version where
418 * the SHIM sets a memory region in the grub binary to readonly when the EFI_MEMORY_ATTRIBUTE protocol is present
419 * but grub tries to write to that region for whatever reason (this seems to be fixed with more recent
420 * guests like ArchLinux or Debian 12 because these still boot with the protocol exposed).
421 * Disabling the protocol also requires disabling mapping the unused memory as NX as the DXE core has
422 * no means to disable the NX when it loads drivers during the discovery phase if the protocol is not exposed.
423 * Not setting the PcdRemapUnusedMemoryNx to FALSE here as I would have to replicate this comment in the DSC file.
424 *
425 * I'd really like to leave them enabled alas OL9 and Ubuntu 23.04 are pretty important ARM guests, sigh...
426 *
427 * The following is an excerpt from the exception with a debug firmware build (for the sake of completeness):
428 * BdsDxe: starting Boot0004 "Oracle Linux" from HD(1,GPT,13CF6614-34C0-46C2-A049-DF0B6B8E67C1,0x800,0x12C000)/\EFI\redhat\shimaa64.efi
429 * InstallProtocolInterface: 605DAB50-E046-4300-ABB6-3DD810DD8B23 F7DC31E8
430 * FSOpen: Open '\EFI\redhat\grubaa64.efi' Success
431 * SetMemoryAttributes: BaseAddress == 0xF77C7000, Length == 0x29F000, Attributes == 0x4000
432 * ClearMemoryAttributes: BaseAddress == 0xF77C7000, Length == 0x29F000, Attributes == 0x22000
433 * SetMemoryAttributes: BaseAddress == 0xF77C8000, Length == 0x1C000, Attributes == 0x20000
434 * ClearMemoryAttributes: BaseAddress == 0xF77C8000, Length == 0x1C000, Attributes == 0x6000
435 * SetMemoryAttributes: BaseAddress == 0xF77E4000, Length == 0x11000, Attributes == 0x4000
436 * ClearMemoryAttributes: BaseAddress == 0xF77E4000, Length == 0x11000, Attributes == 0x22000
437 * SetMemoryAttributes: BaseAddress == 0xF77F5000, Length == 0x26E000, Attributes == 0x4000
438 * ClearMemoryAttributes: BaseAddress == 0xF77F5000, Length == 0x26E000, Attributes == 0x22000
439 * SetMemoryAttributes: BaseAddress == 0xF7A63000, Length == 0x1000, Attributes == 0x24000
440 * ClearMemoryAttributes: BaseAddress == 0xF7A63000, Length == 0x1000, Attributes == 0x2000
441 * SetMemoryAttributes: BaseAddress == 0xF7A64000, Length == 0x1000, Attributes == 0x24000
442 * ClearMemoryAttributes: BaseAddress == 0xF7A64000, Length == 0x1000, Attributes == 0x2000
443 * SetMemoryAttributes: BaseAddress == 0xF77AE000, Length == 0xA000, Attributes == 0x20000
444 * ClearMemoryAttributes: BaseAddress == 0xF77AE000, Length == 0xA000, Attributes == 0x6000
445 * SetMemoryAttributes: BaseAddress == 0xF77B8000, Length == 0x1000, Attributes == 0x24000
446 * ClearMemoryAttributes: BaseAddress == 0xF77B8000, Length == 0x1000, Attributes == 0x2000
447 * SetMemoryAttributes: BaseAddress == 0xF77B9000, Length == 0x1000, Attributes == 0x24000
448 * ClearMemoryAttributes: BaseAddress == 0xF77B9000, Length == 0x1000, Attributes == 0x2000
449 * SetMemoryAttributes: BaseAddress == 0xF77BA000, Length == 0x1000, Attributes == 0x4000
450 * ClearMemoryAttributes: BaseAddress == 0xF77BA000, Length ==== 0x22000
451 * SetMemoryAttributes: BaseAddress == 0xF77BB000, Length == 0x1000, Attributes == 0x4000
452 * ClearMemoryAttributes: BaseAddress == 0xF77BB000, Length == 0x1000, Attributes == 0x22000
453 * SetMemoryAttributes: BaseAddress == 0xF77BC000, Length == 0x1000, Attributes == 0x20000 <= This is where the region is marked as readonly
454 * ClearMemoryAttributes: BaseAddress == 0xF77BC000, Length == 0x1000, Attributes == 0x6000
455 *
456 *
457 * Synchronous Exception at 0x00000000F77D889C
458 * PC 0x0000F77D889C
459 * PC 0x0000F77DAD90
460 * PC 0x0000F77DD240
461 * PC 0x0000F77DDD2C
462 * PC 0x0000F7D2F8D4
463 * PC 0x0000F7D2F984
464 * PC 0x0000F7D2D4F0
465 * PC 0x0000F7D2D030
466 * PC 0x0000FF174DDC (0x0000FF16D000+0x00007DDC) [ 1] DxeCore.dll
467 * PC 0x0000FB72F8AC (0x0000FB71C000+0x000138AC) [ 2] BdsDxe.dll
468 * PC 0x0000FB71E234 (0x0000FB71C000+0x00002234) [ 2] BdsDxe.dll
469 * PC 0x0000FB71FAB8 (0x0000FB71C000+0x00003AB8) [ 2] BdsDxe.dll
470 * PC 0x0000FF16FB78 (0x0000FF16D000+0x00002B78) [ 3] DxeCore.dll
471 * PC 0x0000FF16E9DC (0x0000FF16D000+0x000019DC) [ 3] DxeCore.dll
472 * PC 0x0000FF16E028 (0x0000FF16D000+0x00001028) [ 3] DxeCore.dll
473 * [...]
474 * X0 0x00000000F77BC8A0 X1 0x0000000000000000 X2 0x000000000000000D X3 0x00000000F77E60D8
475 * X4 0x00000000F77BC860 X5 0x00000000F77AE000 X6 0x00000000F77BA000 X7 0x00000000F77E5110
476 * X8 0x00000000F77E5108 X9 0x00000000F77E5110 X10 0x00000000F77E5120 X11 0x00000000F77E5110
477 * X12 0x00000000F77BCFFF X13 0x0000000000000008 X14 0x0000000000000000 X15 0x0000000000000000
478 * X16 0x0000000088660397 X17 0x0000000031550F3D X18 0x0000000000000011 X19 0x00000000F7DAF000
479 * X20 0x0000000000000000 X21 0x00000000F7DC3000 X22 0x00000000F9E3CA98 X3 0x00000000FF16CB40
480 * X24 0x00000000F9E3CA98 X25 0x00000000F7DC3210 X26 0x00000000F7DC3218 X27 0x00000000F7DC3208
481 * X28 0x00000000F7DC3220 FP 0x00000000FF16C990 LR 0x00000000F77DAD90
482 * [...]
483 * SP 0x00000000FF16C990 ELR 0x00000000F77D889C SPSR 0x60000205 FPSR 0x00000000
484 * ESR 0x9600004F FAR 0x00000000F77BC910 <= FAR holds the faulting virtual address
485 *
486 * ESR : EC 0x25 IL 0x1 ISS 0x0000004F
487 *
488 * Data abort: Permission fault, third level
489 */
490#endif
491#ifndef VBOX
492 //
493 // Once we install the CPU arch protocol, the DXE core's memory
494 // protection routines will invoke them to manage the permissions of page
495 // allocations as they are created. Given that this includes pages
496 // allocated for page tables by this driver, we must ensure that unused
497 // memory is mapped with the same permissions as boot services data
498 // regions. Otherwise, we may end up with unbounded recursion, due to the
499 // fact that updating permissions on a newly allocated page table may trigger
500 // a block entry split, which triggers a page table allocation, etc etc
501 //
502 if (FeaturePcdGet (PcdRemapUnusedMemoryNx)) {
503 RemapUnusedMemoryNx ();
504 }
505#endif
506
507 CpuHandle = NULL;
508
509 Status = gBS->InstallMultipleProtocolInterfaces (
510 &CpuHandle,
511 &gEfiCpuArchProtocolGuid,
512 &mCpu,
513#ifndef VBOX
514 &gEfiMemoryAttributeProtocolGuid,
515 &mMemoryAttribute,
516#endif
517 NULL
518 );
519 if (EFI_ERROR (Status)) {
520 ASSERT_EFI_ERROR (Status);
521 return Status;
522 }
523
524 //
525 // Make sure GCD and MMU settings match. This API calls gDS->SetMemorySpaceAttributes ()
526 // and that calls EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes, so this code needs to go
527 // after the protocol is installed
528 //
529 mIsFlushingGCD = TRUE;
530 SyncCacheConfig (&mCpu);
531 mIsFlushingGCD = FALSE;
532
533 //
534 // Setup a callback for idle events
535 //
536 Status = gBS->CreateEventEx (
537 EVT_NOTIFY_SIGNAL,
538 TPL_NOTIFY,
539 IdleLoopEventCallback,
540 NULL,
541 &gIdleLoopEventGuid,
542 &IdleLoopEvent
543 );
544 ASSERT_EFI_ERROR (Status);
545
546 //
547 // Interrupts should only be enabled on the CPU side after the GIC driver has
548 // configured and deasserted all incoming interrupt lines. So keep interrupts
549 // masked until the gHardwareInterruptProtocolGuid protocol appears.
550 //
551 EfiCreateProtocolNotifyEvent (
552 &gHardwareInterruptProtocolGuid,
553 TPL_CALLBACK,
554 HardwareInterruptProtocolNotify,
555 NULL,
556 &mHardwareInterruptProtocolNotifyEventRegistration
557 );
558
559 return EFI_SUCCESS;
560}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette