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