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