VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/ArmPkg/Drivers/CpuDxe/CpuDxe.c@ 101489

Last change on this file since 101489 was 101489, checked in by vboxsync, 15 months ago

Devices/EFI/Firmware/ArmPkg/CpuDxe: Disable exposing the EFI_MEMORY_ATTRIBUTE_PROTOCOL added with edk2-stable202308 as it breaks booting Oracle Linux 9 and Ubuntu Server 23.04 aarch64 guests (explanation in the code), bugref:4643

  • Property svn:eol-style set to native
File size: 16.5 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/**
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**/
45EFI_STATUS
46EFIAPI
47CpuFlushCpuDataCache (
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**/
80EFI_STATUS
81EFIAPI
82CpuEnableInterrupt (
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**/
100EFI_STATUS
101EFIAPI
102CpuDisableInterrupt (
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**/
124EFI_STATUS
125EFIAPI
126CpuGetInterruptState (
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**/
155EFI_STATUS
156EFIAPI
157CpuInit (
158 IN EFI_CPU_ARCH_PROTOCOL *This,
159 IN EFI_CPU_INIT_TYPE InitType
160 )
161{
162 return EFI_UNSUPPORTED;
163}
164
165EFI_STATUS
166EFIAPI
167CpuRegisterInterruptHandler (
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
176EFI_STATUS
177EFIAPI
178CpuGetTimerValue (
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**/
196VOID
197EFIAPI
198IdleLoopEventCallback (
199 IN EFI_EVENT Event,
200 IN VOID *Context
201 )
202{
203 CpuSleep ();
204}
205
206//
207// Globals used to initialize the protocol
208//
209EFI_HANDLE mCpuHandle = NULL;
210EFI_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
223STATIC
224VOID
225InitializeDma (
226 IN OUT EFI_CPU_ARCH_PROTOCOL *CpuArchProtocol
227 )
228{
229 CpuArchProtocol->DmaBufferAlignment = ArmCacheWritebackGranule ();
230}
231
232#ifndef VBOX
233/**
234 Map all EfiConventionalMemory regions in the memory map with NX
235 attributes so that allocating or freeing EfiBootServicesData regions
236 does not result in changes to memory permission attributes.
237
238**/
239STATIC
240VOID
241RemapUnusedMemoryNx (
242 VOID
243 )
244{
245 UINT64 TestBit;
246 UINTN MemoryMapSize;
247 UINTN MapKey;
248 UINTN DescriptorSize;
249 UINT32 DescriptorVersion;
250 EFI_MEMORY_DESCRIPTOR *MemoryMap;
251 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
252 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
253 EFI_STATUS Status;
254
255 TestBit = LShiftU64 (1, EfiBootServicesData);
256 if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) == 0) {
257 return;
258 }
259
260 MemoryMapSize = 0;
261 MemoryMap = NULL;
262
263 Status = gBS->GetMemoryMap (
264 &MemoryMapSize,
265 MemoryMap,
266 &MapKey,
267 &DescriptorSize,
268 &DescriptorVersion
269 );
270 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
271 do {
272 MemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocatePool (MemoryMapSize);
273 ASSERT (MemoryMap != NULL);
274 Status = gBS->GetMemoryMap (
275 &MemoryMapSize,
276 MemoryMap,
277 &MapKey,
278 &DescriptorSize,
279 &DescriptorVersion
280 );
281 if (EFI_ERROR (Status)) {
282 FreePool (MemoryMap);
283 }
284 } while (Status == EFI_BUFFER_TOO_SMALL);
285
286 ASSERT_EFI_ERROR (Status);
287
288 MemoryMapEntry = MemoryMap;
289 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
290 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
291 if (MemoryMapEntry->Type == EfiConventionalMemory) {
292 ArmSetMemoryAttributes (
293 MemoryMapEntry->PhysicalStart,
294 EFI_PAGES_TO_SIZE (MemoryMapEntry->NumberOfPages),
295 EFI_MEMORY_XP,
296 EFI_MEMORY_XP
297 );
298 }
299
300 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
301 }
302}
303#endif
304
305EFI_STATUS
306CpuDxeInitialize (
307 IN EFI_HANDLE ImageHandle,
308 IN EFI_SYSTEM_TABLE *SystemTable
309 )
310{
311 EFI_STATUS Status;
312 EFI_EVENT IdleLoopEvent;
313
314 InitializeExceptions (&mCpu);
315
316 InitializeDma (&mCpu);
317
318#ifdef VBOX
319 /** @todo r=aeichner
320 * Exposing the EFI_MEMORY_ATTRIBUTE protocol (added with edk2-stable202308) breaks OL9 and
321 * Ubuntu 23.04 server guests at least. They use a shima64.efi and grubaa64.efi version where
322 * the SHIM sets a memory region in the grub binary to readonly when the EFI_MEMORY_ATTRIBUTE protocol is present
323 * but grub tries to write to that region for whatever reason (this seems to be fixed with more recent
324 * guests like ArchLinux or Debian 12 because these still boot with the protocol exposed).
325 * Disabling the protocol also requires disabling mapping the unused memory as NX as the DXE core has
326 * no means to disable the NX when it loads drivers during the discovery phase if the protocol is not exposed.
327 * Not setting the PcdRemapUnusedMemoryNx to FALSE here as I would have to replicate this comment in the DSC file.
328 *
329 * I'd really like to leave them enabled alas OL9 and Ubuntu 23.04 are pretty important ARM guests, sigh...
330 *
331 * The following is an excerpt from the exception with a debug firmware build (for the sake of completeness):
332 * BdsDxe: starting Boot0004 "Oracle Linux" from HD(1,GPT,13CF6614-34C0-46C2-A049-DF0B6B8E67C1,0x800,0x12C000)/\EFI\redhat\shimaa64.efi
333 * InstallProtocolInterface: 605DAB50-E046-4300-ABB6-3DD810DD8B23 F7DC31E8
334 * FSOpen: Open '\EFI\redhat\grubaa64.efi' Success
335 * SetMemoryAttributes: BaseAddress == 0xF77C7000, Length == 0x29F000, Attributes == 0x4000
336 * ClearMemoryAttributes: BaseAddress == 0xF77C7000, Length == 0x29F000, Attributes == 0x22000
337 * SetMemoryAttributes: BaseAddress == 0xF77C8000, Length == 0x1C000, Attributes == 0x20000
338 * ClearMemoryAttributes: BaseAddress == 0xF77C8000, Length == 0x1C000, Attributes == 0x6000
339 * SetMemoryAttributes: BaseAddress == 0xF77E4000, Length == 0x11000, Attributes == 0x4000
340 * ClearMemoryAttributes: BaseAddress == 0xF77E4000, Length == 0x11000, Attributes == 0x22000
341 * SetMemoryAttributes: BaseAddress == 0xF77F5000, Length == 0x26E000, Attributes == 0x4000
342 * ClearMemoryAttributes: BaseAddress == 0xF77F5000, Length == 0x26E000, Attributes == 0x22000
343 * SetMemoryAttributes: BaseAddress == 0xF7A63000, Length == 0x1000, Attributes == 0x24000
344 * ClearMemoryAttributes: BaseAddress == 0xF7A63000, Length == 0x1000, Attributes == 0x2000
345 * SetMemoryAttributes: BaseAddress == 0xF7A64000, Length == 0x1000, Attributes == 0x24000
346 * ClearMemoryAttributes: BaseAddress == 0xF7A64000, Length == 0x1000, Attributes == 0x2000
347 * SetMemoryAttributes: BaseAddress == 0xF77AE000, Length == 0xA000, Attributes == 0x20000
348 * ClearMemoryAttributes: BaseAddress == 0xF77AE000, Length == 0xA000, Attributes == 0x6000
349 * SetMemoryAttributes: BaseAddress == 0xF77B8000, Length == 0x1000, Attributes == 0x24000
350 * ClearMemoryAttributes: BaseAddress == 0xF77B8000, Length == 0x1000, Attributes == 0x2000
351 * SetMemoryAttributes: BaseAddress == 0xF77B9000, Length == 0x1000, Attributes == 0x24000
352 * ClearMemoryAttributes: BaseAddress == 0xF77B9000, Length == 0x1000, Attributes == 0x2000
353 * SetMemoryAttributes: BaseAddress == 0xF77BA000, Length == 0x1000, Attributes == 0x4000
354 * ClearMemoryAttributes: BaseAddress == 0xF77BA000, Length ==== 0x22000
355 * SetMemoryAttributes: BaseAddress == 0xF77BB000, Length == 0x1000, Attributes == 0x4000
356 * ClearMemoryAttributes: BaseAddress == 0xF77BB000, Length == 0x1000, Attributes == 0x22000
357 * SetMemoryAttributes: BaseAddress == 0xF77BC000, Length == 0x1000, Attributes == 0x20000 <= This is where the region is marked as readonly
358 * ClearMemoryAttributes: BaseAddress == 0xF77BC000, Length == 0x1000, Attributes == 0x6000
359 *
360 *
361 * Synchronous Exception at 0x00000000F77D889C
362 * PC 0x0000F77D889C
363 * PC 0x0000F77DAD90
364 * PC 0x0000F77DD240
365 * PC 0x0000F77DDD2C
366 * PC 0x0000F7D2F8D4
367 * PC 0x0000F7D2F984
368 * PC 0x0000F7D2D4F0
369 * PC 0x0000F7D2D030
370 * PC 0x0000FF174DDC (0x0000FF16D000+0x00007DDC) [ 1] DxeCore.dll
371 * PC 0x0000FB72F8AC (0x0000FB71C000+0x000138AC) [ 2] BdsDxe.dll
372 * PC 0x0000FB71E234 (0x0000FB71C000+0x00002234) [ 2] BdsDxe.dll
373 * PC 0x0000FB71FAB8 (0x0000FB71C000+0x00003AB8) [ 2] BdsDxe.dll
374 * PC 0x0000FF16FB78 (0x0000FF16D000+0x00002B78) [ 3] DxeCore.dll
375 * PC 0x0000FF16E9DC (0x0000FF16D000+0x000019DC) [ 3] DxeCore.dll
376 * PC 0x0000FF16E028 (0x0000FF16D000+0x00001028) [ 3] DxeCore.dll
377 * [...]
378 * X0 0x00000000F77BC8A0 X1 0x0000000000000000 X2 0x000000000000000D X3 0x00000000F77E60D8
379 * X4 0x00000000F77BC860 X5 0x00000000F77AE000 X6 0x00000000F77BA000 X7 0x00000000F77E5110
380 * X8 0x00000000F77E5108 X9 0x00000000F77E5110 X10 0x00000000F77E5120 X11 0x00000000F77E5110
381 * X12 0x00000000F77BCFFF X13 0x0000000000000008 X14 0x0000000000000000 X15 0x0000000000000000
382 * X16 0x0000000088660397 X17 0x0000000031550F3D X18 0x0000000000000011 X19 0x00000000F7DAF000
383 * X20 0x0000000000000000 X21 0x00000000F7DC3000 X22 0x00000000F9E3CA98 X3 0x00000000FF16CB40
384 * X24 0x00000000F9E3CA98 X25 0x00000000F7DC3210 X26 0x00000000F7DC3218 X27 0x00000000F7DC3208
385 * X28 0x00000000F7DC3220 FP 0x00000000FF16C990 LR 0x00000000F77DAD90
386 * [...]
387 * SP 0x00000000FF16C990 ELR 0x00000000F77D889C SPSR 0x60000205 FPSR 0x00000000
388 * ESR 0x9600004F FAR 0x00000000F77BC910 <= FAR holds the faulting virtual address
389 *
390 * ESR : EC 0x25 IL 0x1 ISS 0x0000004F
391 *
392 * Data abort: Permission fault, third level
393 */
394#endif
395#ifndef VBOX
396 //
397 // Once we install the CPU arch protocol, the DXE core's memory
398 // protection routines will invoke them to manage the permissions of page
399 // allocations as they are created. Given that this includes pages
400 // allocated for page tables by this driver, we must ensure that unused
401 // memory is mapped with the same permissions as boot services data
402 // regions. Otherwise, we may end up with unbounded recursion, due to the
403 // fact that updating permissions on a newly allocated page table may trigger
404 // a block entry split, which triggers a page table allocation, etc etc
405 //
406 if (FeaturePcdGet (PcdRemapUnusedMemoryNx)) {
407 RemapUnusedMemoryNx ();
408 }
409#endif
410
411 Status = gBS->InstallMultipleProtocolInterfaces (
412 &mCpuHandle,
413 &gEfiCpuArchProtocolGuid,
414 &mCpu,
415#ifndef VBOX
416 &gEfiMemoryAttributeProtocolGuid,
417 &mMemoryAttribute,
418#endif
419 NULL
420 );
421
422 //
423 // Make sure GCD and MMU settings match. This API calls gDS->SetMemorySpaceAttributes ()
424 // and that calls EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes, so this code needs to go
425 // after the protocol is installed
426 //
427 mIsFlushingGCD = TRUE;
428 SyncCacheConfig (&mCpu);
429 mIsFlushingGCD = FALSE;
430
431 //
432 // Setup a callback for idle events
433 //
434 Status = gBS->CreateEventEx (
435 EVT_NOTIFY_SIGNAL,
436 TPL_NOTIFY,
437 IdleLoopEventCallback,
438 NULL,
439 &gIdleLoopEventGuid,
440 &IdleLoopEvent
441 );
442 ASSERT_EFI_ERROR (Status);
443
444 return Status;
445}
Note: See TracBrowser for help on using the repository browser.

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