]> git.proxmox.com Git - mirror_edk2.git/blob - IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c
IntelSiliconPkg IntelVTdDxe: Use TPL to protect list/engine operation
[mirror_edk2.git] / IntelSiliconPkg / Feature / VTd / IntelVTdDxe / DmaProtection.c
1 /** @file
2
3 Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php.
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 **/
13
14 #include "DmaProtection.h"
15
16 UINT64 mBelow4GMemoryLimit;
17 UINT64 mAbove4GMemoryLimit;
18
19 EDKII_PLATFORM_VTD_POLICY_PROTOCOL *mPlatformVTdPolicy;
20
21 /**
22 return the UEFI memory information.
23
24 @param[out] Below4GMemoryLimit The below 4GiB memory limit
25 @param[out] Above4GMemoryLimit The above 4GiB memory limit
26 **/
27 VOID
28 ReturnUefiMemoryMap (
29 OUT UINT64 *Below4GMemoryLimit,
30 OUT UINT64 *Above4GMemoryLimit
31 )
32 {
33 EFI_STATUS Status;
34 EFI_MEMORY_DESCRIPTOR *EfiMemoryMap;
35 EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd;
36 EFI_MEMORY_DESCRIPTOR *EfiEntry;
37 EFI_MEMORY_DESCRIPTOR *NextEfiEntry;
38 EFI_MEMORY_DESCRIPTOR TempEfiEntry;
39 UINTN EfiMemoryMapSize;
40 UINTN EfiMapKey;
41 UINTN EfiDescriptorSize;
42 UINT32 EfiDescriptorVersion;
43 UINT64 MemoryBlockLength;
44
45 *Below4GMemoryLimit = 0;
46 *Above4GMemoryLimit = 0;
47
48 //
49 // Get the EFI memory map.
50 //
51 EfiMemoryMapSize = 0;
52 EfiMemoryMap = NULL;
53 Status = gBS->GetMemoryMap (
54 &EfiMemoryMapSize,
55 EfiMemoryMap,
56 &EfiMapKey,
57 &EfiDescriptorSize,
58 &EfiDescriptorVersion
59 );
60 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
61
62 do {
63 //
64 // Use size returned back plus 1 descriptor for the AllocatePool.
65 // We don't just multiply by 2 since the "for" loop below terminates on
66 // EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwize
67 // we process bogus entries and create bogus E820 entries.
68 //
69 EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (EfiMemoryMapSize);
70 ASSERT (EfiMemoryMap != NULL);
71 Status = gBS->GetMemoryMap (
72 &EfiMemoryMapSize,
73 EfiMemoryMap,
74 &EfiMapKey,
75 &EfiDescriptorSize,
76 &EfiDescriptorVersion
77 );
78 if (EFI_ERROR (Status)) {
79 FreePool (EfiMemoryMap);
80 }
81 } while (Status == EFI_BUFFER_TOO_SMALL);
82
83 ASSERT_EFI_ERROR (Status);
84
85 //
86 // Sort memory map from low to high
87 //
88 EfiEntry = EfiMemoryMap;
89 NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
90 EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
91 while (EfiEntry < EfiMemoryMapEnd) {
92 while (NextEfiEntry < EfiMemoryMapEnd) {
93 if (EfiEntry->PhysicalStart > NextEfiEntry->PhysicalStart) {
94 CopyMem (&TempEfiEntry, EfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
95 CopyMem (EfiEntry, NextEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
96 CopyMem (NextEfiEntry, &TempEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
97 }
98
99 NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (NextEfiEntry, EfiDescriptorSize);
100 }
101
102 EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
103 NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
104 }
105
106 //
107 //
108 //
109 DEBUG ((DEBUG_INFO, "MemoryMap:\n"));
110 EfiEntry = EfiMemoryMap;
111 EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
112 while (EfiEntry < EfiMemoryMapEnd) {
113 MemoryBlockLength = (UINT64) (LShiftU64 (EfiEntry->NumberOfPages, 12));
114 DEBUG ((DEBUG_INFO, "Entry(0x%02x) 0x%016lx - 0x%016lx\n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->PhysicalStart + MemoryBlockLength));
115 switch (EfiEntry->Type) {
116 case EfiLoaderCode:
117 case EfiLoaderData:
118 case EfiBootServicesCode:
119 case EfiBootServicesData:
120 case EfiConventionalMemory:
121 case EfiRuntimeServicesCode:
122 case EfiRuntimeServicesData:
123 case EfiACPIReclaimMemory:
124 case EfiACPIMemoryNVS:
125 case EfiReservedMemoryType:
126 if ((EfiEntry->PhysicalStart + MemoryBlockLength) <= BASE_1MB) {
127 //
128 // Skip the memory block is under 1MB
129 //
130 } else if (EfiEntry->PhysicalStart >= BASE_4GB) {
131 if (*Above4GMemoryLimit < EfiEntry->PhysicalStart + MemoryBlockLength) {
132 *Above4GMemoryLimit = EfiEntry->PhysicalStart + MemoryBlockLength;
133 }
134 } else {
135 if (*Below4GMemoryLimit < EfiEntry->PhysicalStart + MemoryBlockLength) {
136 *Below4GMemoryLimit = EfiEntry->PhysicalStart + MemoryBlockLength;
137 }
138 }
139 break;
140 }
141 EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
142 }
143
144 FreePool (EfiMemoryMap);
145
146 DEBUG ((DEBUG_INFO, "Result:\n"));
147 DEBUG ((DEBUG_INFO, "Below4GMemoryLimit: 0x%016lx\n", *Below4GMemoryLimit));
148 DEBUG ((DEBUG_INFO, "Above4GMemoryLimit: 0x%016lx\n", *Above4GMemoryLimit));
149
150 return ;
151 }
152
153 /**
154 The scan bus callback function to always enable page attribute.
155
156 @param[in] Context The context of the callback.
157 @param[in] Segment The segment of the source.
158 @param[in] Bus The bus of the source.
159 @param[in] Device The device of the source.
160 @param[in] Function The function of the source.
161
162 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device.
163 **/
164 EFI_STATUS
165 EFIAPI
166 ScanBusCallbackAlwaysEnablePageAttribute (
167 IN VOID *Context,
168 IN UINT16 Segment,
169 IN UINT8 Bus,
170 IN UINT8 Device,
171 IN UINT8 Function
172 )
173 {
174 VTD_SOURCE_ID SourceId;
175 EFI_STATUS Status;
176
177 SourceId.Bits.Bus = Bus;
178 SourceId.Bits.Device = Device;
179 SourceId.Bits.Function = Function;
180 Status = AlwaysEnablePageAttribute (Segment, SourceId);
181 return Status;
182 }
183
184 /**
185 Always enable the VTd page attribute for the device in the DeviceScope.
186
187 @param[in] DeviceScope the input device scope data structure
188
189 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device in the device scope.
190 **/
191 EFI_STATUS
192 AlwaysEnablePageAttributeDeviceScope (
193 IN EDKII_PLATFORM_VTD_DEVICE_SCOPE *DeviceScope
194 )
195 {
196 UINT8 Bus;
197 UINT8 Device;
198 UINT8 Function;
199 VTD_SOURCE_ID SourceId;
200 UINT8 SecondaryBusNumber;
201 EFI_STATUS Status;
202
203 Status = GetPciBusDeviceFunction (DeviceScope->SegmentNumber, &DeviceScope->DeviceScope, &Bus, &Device, &Function);
204
205 if (DeviceScope->DeviceScope.Type == EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE) {
206 //
207 // Need scan the bridge and add all devices.
208 //
209 SecondaryBusNumber = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(DeviceScope->SegmentNumber, Bus, Device, Function, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
210 Status = ScanPciBus (NULL, DeviceScope->SegmentNumber, SecondaryBusNumber, ScanBusCallbackAlwaysEnablePageAttribute);
211 return Status;
212 } else {
213 SourceId.Bits.Bus = Bus;
214 SourceId.Bits.Device = Device;
215 SourceId.Bits.Function = Function;
216 Status = AlwaysEnablePageAttribute (DeviceScope->SegmentNumber, SourceId);
217 return Status;
218 }
219 }
220
221 /**
222 Always enable the VTd page attribute for the device matching DeviceId.
223
224 @param[in] PciDeviceId the input PCI device ID
225
226 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device matching DeviceId.
227 **/
228 EFI_STATUS
229 AlwaysEnablePageAttributePciDeviceId (
230 IN EDKII_PLATFORM_VTD_PCI_DEVICE_ID *PciDeviceId
231 )
232 {
233 UINTN VtdIndex;
234 UINTN PciIndex;
235 PCI_DEVICE_DATA *PciDeviceData;
236 EFI_STATUS Status;
237
238 for (VtdIndex = 0; VtdIndex < mVtdUnitNumber; VtdIndex++) {
239 for (PciIndex = 0; PciIndex < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceDataNumber; PciIndex++) {
240 PciDeviceData = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[PciIndex];
241
242 if (((PciDeviceId->VendorId == 0xFFFF) || (PciDeviceId->VendorId == PciDeviceData->PciDeviceId.VendorId)) &&
243 ((PciDeviceId->DeviceId == 0xFFFF) || (PciDeviceId->DeviceId == PciDeviceData->PciDeviceId.DeviceId)) &&
244 ((PciDeviceId->RevisionId == 0xFF) || (PciDeviceId->RevisionId == PciDeviceData->PciDeviceId.RevisionId)) &&
245 ((PciDeviceId->SubsystemVendorId == 0xFFFF) || (PciDeviceId->SubsystemVendorId == PciDeviceData->PciDeviceId.SubsystemVendorId)) &&
246 ((PciDeviceId->SubsystemDeviceId == 0xFFFF) || (PciDeviceId->SubsystemDeviceId == PciDeviceData->PciDeviceId.SubsystemDeviceId)) ) {
247 Status = AlwaysEnablePageAttribute (mVtdUnitInformation[VtdIndex].Segment, PciDeviceData->PciSourceId);
248 if (EFI_ERROR(Status)) {
249 continue;
250 }
251 }
252 }
253 }
254 return EFI_SUCCESS;
255 }
256
257 /**
258 Always enable the VTd page attribute for the device.
259
260 @param[in] DeviceInfo the exception device information
261
262 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device in the device info.
263 **/
264 EFI_STATUS
265 AlwaysEnablePageAttributeExceptionDeviceInfo (
266 IN EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO *DeviceInfo
267 )
268 {
269 switch (DeviceInfo->Type) {
270 case EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_DEVICE_SCOPE:
271 return AlwaysEnablePageAttributeDeviceScope ((VOID *)(DeviceInfo + 1));
272 case EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_PCI_DEVICE_ID:
273 return AlwaysEnablePageAttributePciDeviceId ((VOID *)(DeviceInfo + 1));
274 default:
275 return EFI_UNSUPPORTED;
276 }
277 }
278
279 /**
280 Initialize platform VTd policy.
281 **/
282 VOID
283 InitializePlatformVTdPolicy (
284 VOID
285 )
286 {
287 EFI_STATUS Status;
288 UINTN DeviceInfoCount;
289 VOID *DeviceInfo;
290 EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO *ThisDeviceInfo;
291 UINTN Index;
292
293 //
294 // It is optional.
295 //
296 Status = gBS->LocateProtocol (
297 &gEdkiiPlatformVTdPolicyProtocolGuid,
298 NULL,
299 (VOID **)&mPlatformVTdPolicy
300 );
301 if (!EFI_ERROR(Status)) {
302 DEBUG ((DEBUG_INFO, "InitializePlatformVTdPolicy\n"));
303 Status = mPlatformVTdPolicy->GetExceptionDeviceList (mPlatformVTdPolicy, &DeviceInfoCount, &DeviceInfo);
304 if (!EFI_ERROR(Status)) {
305 ThisDeviceInfo = DeviceInfo;
306 for (Index = 0; Index < DeviceInfoCount; Index++) {
307 if (ThisDeviceInfo->Type == EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_END) {
308 break;
309 }
310 AlwaysEnablePageAttributeExceptionDeviceInfo (ThisDeviceInfo);
311 ThisDeviceInfo = (VOID *)((UINTN)ThisDeviceInfo + ThisDeviceInfo->Length);
312 }
313 FreePool (DeviceInfo);
314 }
315 }
316 }
317
318 /**
319 Setup VTd engine.
320 **/
321 VOID
322 SetupVtd (
323 VOID
324 )
325 {
326 EFI_STATUS Status;
327 VOID *PciEnumerationComplete;
328 UINTN Index;
329 UINT64 Below4GMemoryLimit;
330 UINT64 Above4GMemoryLimit;
331
332 //
333 // PCI Enumeration must be done
334 //
335 Status = gBS->LocateProtocol (
336 &gEfiPciEnumerationCompleteProtocolGuid,
337 NULL,
338 &PciEnumerationComplete
339 );
340 ASSERT_EFI_ERROR (Status);
341
342 ReturnUefiMemoryMap (&Below4GMemoryLimit, &Above4GMemoryLimit);
343 Below4GMemoryLimit = ALIGN_VALUE_UP(Below4GMemoryLimit, SIZE_256MB);
344 DEBUG ((DEBUG_INFO, " Adjusted Below4GMemoryLimit: 0x%016lx\n", Below4GMemoryLimit));
345
346 mBelow4GMemoryLimit = Below4GMemoryLimit;
347 mAbove4GMemoryLimit = Above4GMemoryLimit;
348
349 //
350 // 1. setup
351 //
352 DEBUG ((DEBUG_INFO, "ParseDmarAcpiTable\n"));
353 Status = ParseDmarAcpiTableDrhd ();
354 if (EFI_ERROR (Status)) {
355 return;
356 }
357 DEBUG ((DEBUG_INFO, "PrepareVtdConfig\n"));
358 PrepareVtdConfig ();
359
360 //
361 // 2. initialization
362 //
363 DEBUG ((DEBUG_INFO, "SetupTranslationTable\n"));
364 Status = SetupTranslationTable ();
365 if (EFI_ERROR (Status)) {
366 return;
367 }
368
369 InitializePlatformVTdPolicy ();
370
371 ParseDmarAcpiTableRmrr ();
372
373 for (Index = 0; Index < mVtdUnitNumber; Index++) {
374 DEBUG ((DEBUG_INFO,"VTD Unit %d (Segment: %04x)\n", Index, mVtdUnitInformation[Index].Segment));
375 if (mVtdUnitInformation[Index].ExtRootEntryTable != NULL) {
376 DumpDmarExtContextEntryTable (mVtdUnitInformation[Index].ExtRootEntryTable);
377 }
378 if (mVtdUnitInformation[Index].RootEntryTable != NULL) {
379 DumpDmarContextEntryTable (mVtdUnitInformation[Index].RootEntryTable);
380 }
381 }
382
383 //
384 // 3. enable
385 //
386 DEBUG ((DEBUG_INFO, "EnableDmar\n"));
387 Status = EnableDmar ();
388 if (EFI_ERROR (Status)) {
389 return;
390 }
391 DEBUG ((DEBUG_INFO, "DumpVtdRegs\n"));
392 DumpVtdRegsAll ();
393 }
394
395 /**
396 Notification function of ACPI Table change.
397
398 This is a notification function registered on ACPI Table change event.
399
400 @param Event Event whose notification function is being invoked.
401 @param Context Pointer to the notification function's context.
402
403 **/
404 VOID
405 EFIAPI
406 AcpiNotificationFunc (
407 IN EFI_EVENT Event,
408 IN VOID *Context
409 )
410 {
411 EFI_STATUS Status;
412
413 Status = GetDmarAcpiTable ();
414 if (EFI_ERROR (Status)) {
415 if (Status == EFI_ALREADY_STARTED) {
416 gBS->CloseEvent (Event);
417 }
418 return;
419 }
420 SetupVtd ();
421 gBS->CloseEvent (Event);
422 }
423
424 /**
425 Exit boot service callback function.
426
427 @param[in] Event The event handle.
428 @param[in] Context The event content.
429 **/
430 VOID
431 EFIAPI
432 OnExitBootServices (
433 IN EFI_EVENT Event,
434 IN VOID *Context
435 )
436 {
437 DEBUG ((DEBUG_INFO, "Vtd OnExitBootServices\n"));
438 DumpVtdRegsAll ();
439
440 if ((PcdGet8(PcdVTdPolicyPropertyMask) & BIT1) == 0) {
441 DisableDmar ();
442 DumpVtdRegsAll ();
443 }
444 }
445
446 /**
447 Legacy boot callback function.
448
449 @param[in] Event The event handle.
450 @param[in] Context The event content.
451 **/
452 VOID
453 EFIAPI
454 OnLegacyBoot (
455 EFI_EVENT Event,
456 VOID *Context
457 )
458 {
459 DEBUG ((DEBUG_INFO, "Vtd OnLegacyBoot\n"));
460 DumpVtdRegsAll ();
461 DisableDmar ();
462 DumpVtdRegsAll ();
463 }
464
465 /**
466 Initialize DMA protection.
467 **/
468 VOID
469 InitializeDmaProtection (
470 VOID
471 )
472 {
473 EFI_STATUS Status;
474 EFI_EVENT ExitBootServicesEvent;
475 EFI_EVENT LegacyBootEvent;
476 EFI_EVENT EventAcpi10;
477 EFI_EVENT EventAcpi20;
478
479 Status = gBS->CreateEventEx (
480 EVT_NOTIFY_SIGNAL,
481 VTD_TPL_LEVEL,
482 AcpiNotificationFunc,
483 NULL,
484 &gEfiAcpi10TableGuid,
485 &EventAcpi10
486 );
487 ASSERT_EFI_ERROR (Status);
488
489 Status = gBS->CreateEventEx (
490 EVT_NOTIFY_SIGNAL,
491 VTD_TPL_LEVEL,
492 AcpiNotificationFunc,
493 NULL,
494 &gEfiAcpi20TableGuid,
495 &EventAcpi20
496 );
497 ASSERT_EFI_ERROR (Status);
498
499 //
500 // Signal the events initially for the case
501 // that DMAR table has been installed.
502 //
503 gBS->SignalEvent (EventAcpi20);
504 gBS->SignalEvent (EventAcpi10);
505
506 Status = gBS->CreateEventEx (
507 EVT_NOTIFY_SIGNAL,
508 TPL_CALLBACK,
509 OnExitBootServices,
510 NULL,
511 &gEfiEventExitBootServicesGuid,
512 &ExitBootServicesEvent
513 );
514 ASSERT_EFI_ERROR (Status);
515
516 Status = EfiCreateEventLegacyBootEx (
517 TPL_CALLBACK,
518 OnLegacyBoot,
519 NULL,
520 &LegacyBootEvent
521 );
522 ASSERT_EFI_ERROR (Status);
523
524 return ;
525 }