3 Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
8 #include "DmaProtection.h"
10 UINT64 mBelow4GMemoryLimit
;
11 UINT64 mAbove4GMemoryLimit
;
13 EDKII_PLATFORM_VTD_POLICY_PROTOCOL
*mPlatformVTdPolicy
;
15 VTD_ACCESS_REQUEST
*mAccessRequest
= NULL
;
16 UINTN mAccessRequestCount
= 0;
17 UINTN mAccessRequestMaxCount
= 0;
20 Append VTd Access Request to global.
22 @param[in] Segment The Segment used to identify a VTd engine.
23 @param[in] SourceId The SourceId used to identify a VTd engine and table entry.
24 @param[in] BaseAddress The base of device memory address to be used as the DMA memory.
25 @param[in] Length The length of device memory address to be used as the DMA memory.
26 @param[in] IoMmuAccess The IOMMU access.
28 @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by BaseAddress and Length.
29 @retval EFI_INVALID_PARAMETER BaseAddress is not IoMmu Page size aligned.
30 @retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned.
31 @retval EFI_INVALID_PARAMETER Length is 0.
32 @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access.
33 @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU.
34 @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by BaseAddress and Length.
35 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access.
36 @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation.
40 RequestAccessAttribute (
42 IN VTD_SOURCE_ID SourceId
,
43 IN UINT64 BaseAddress
,
48 VTD_ACCESS_REQUEST
*NewAccessRequest
;
52 // Optimization for memory.
54 // If the last record is to IoMmuAccess=0,
55 // Check previous records and remove the matched entry.
57 if (IoMmuAccess
== 0) {
58 for (Index
= 0; Index
< mAccessRequestCount
; Index
++) {
59 if ((mAccessRequest
[Index
].Segment
== Segment
) &&
60 (mAccessRequest
[Index
].SourceId
.Uint16
== SourceId
.Uint16
) &&
61 (mAccessRequest
[Index
].BaseAddress
== BaseAddress
) &&
62 (mAccessRequest
[Index
].Length
== Length
) &&
63 (mAccessRequest
[Index
].IoMmuAccess
!= 0)) {
65 // Remove this record [Index].
66 // No need to add the new record.
68 if (Index
!= mAccessRequestCount
- 1) {
70 &mAccessRequest
[Index
],
71 &mAccessRequest
[Index
+ 1],
72 sizeof (VTD_ACCESS_REQUEST
) * (mAccessRequestCount
- 1 - Index
)
75 ZeroMem (&mAccessRequest
[mAccessRequestCount
- 1], sizeof(VTD_ACCESS_REQUEST
));
76 mAccessRequestCount
--;
82 if (mAccessRequestCount
>= mAccessRequestMaxCount
) {
83 NewAccessRequest
= AllocateZeroPool (sizeof(*NewAccessRequest
) * (mAccessRequestMaxCount
+ MAX_VTD_ACCESS_REQUEST
));
84 if (NewAccessRequest
== NULL
) {
85 return EFI_OUT_OF_RESOURCES
;
87 mAccessRequestMaxCount
+= MAX_VTD_ACCESS_REQUEST
;
88 if (mAccessRequest
!= NULL
) {
89 CopyMem (NewAccessRequest
, mAccessRequest
, sizeof(*NewAccessRequest
) * mAccessRequestCount
);
90 FreePool (mAccessRequest
);
92 mAccessRequest
= NewAccessRequest
;
95 ASSERT (mAccessRequestCount
< mAccessRequestMaxCount
);
97 mAccessRequest
[mAccessRequestCount
].Segment
= Segment
;
98 mAccessRequest
[mAccessRequestCount
].SourceId
= SourceId
;
99 mAccessRequest
[mAccessRequestCount
].BaseAddress
= BaseAddress
;
100 mAccessRequest
[mAccessRequestCount
].Length
= Length
;
101 mAccessRequest
[mAccessRequestCount
].IoMmuAccess
= IoMmuAccess
;
103 mAccessRequestCount
++;
109 Process Access Requests from before DMAR table is installed.
113 ProcessRequestedAccessAttribute (
120 DEBUG ((DEBUG_INFO
, "ProcessRequestedAccessAttribute ...\n"));
122 for (Index
= 0; Index
< mAccessRequestCount
; Index
++) {
125 "PCI(S%x.B%x.D%x.F%x) ",
126 mAccessRequest
[Index
].Segment
,
127 mAccessRequest
[Index
].SourceId
.Bits
.Bus
,
128 mAccessRequest
[Index
].SourceId
.Bits
.Device
,
129 mAccessRequest
[Index
].SourceId
.Bits
.Function
133 "(0x%lx~0x%lx) - %lx\n",
134 mAccessRequest
[Index
].BaseAddress
,
135 mAccessRequest
[Index
].Length
,
136 mAccessRequest
[Index
].IoMmuAccess
138 Status
= SetAccessAttribute (
139 mAccessRequest
[Index
].Segment
,
140 mAccessRequest
[Index
].SourceId
,
141 mAccessRequest
[Index
].BaseAddress
,
142 mAccessRequest
[Index
].Length
,
143 mAccessRequest
[Index
].IoMmuAccess
145 if (EFI_ERROR (Status
)) {
146 DEBUG ((DEBUG_ERROR
, "SetAccessAttribute %r: ", Status
));
150 if (mAccessRequest
!= NULL
) {
151 FreePool (mAccessRequest
);
153 mAccessRequest
= NULL
;
154 mAccessRequestCount
= 0;
155 mAccessRequestMaxCount
= 0;
157 DEBUG ((DEBUG_INFO
, "ProcessRequestedAccessAttribute Done\n"));
161 return the UEFI memory information.
163 @param[out] Below4GMemoryLimit The below 4GiB memory limit
164 @param[out] Above4GMemoryLimit The above 4GiB memory limit
167 ReturnUefiMemoryMap (
168 OUT UINT64
*Below4GMemoryLimit
,
169 OUT UINT64
*Above4GMemoryLimit
173 EFI_MEMORY_DESCRIPTOR
*EfiMemoryMap
;
174 EFI_MEMORY_DESCRIPTOR
*EfiMemoryMapEnd
;
175 EFI_MEMORY_DESCRIPTOR
*EfiEntry
;
176 EFI_MEMORY_DESCRIPTOR
*NextEfiEntry
;
177 EFI_MEMORY_DESCRIPTOR TempEfiEntry
;
178 UINTN EfiMemoryMapSize
;
180 UINTN EfiDescriptorSize
;
181 UINT32 EfiDescriptorVersion
;
182 UINT64 MemoryBlockLength
;
184 *Below4GMemoryLimit
= 0;
185 *Above4GMemoryLimit
= 0;
188 // Get the EFI memory map.
190 EfiMemoryMapSize
= 0;
192 Status
= gBS
->GetMemoryMap (
197 &EfiDescriptorVersion
199 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
203 // Use size returned back plus 1 descriptor for the AllocatePool.
204 // We don't just multiply by 2 since the "for" loop below terminates on
205 // EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwize
206 // we process bogus entries and create bogus E820 entries.
208 EfiMemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (EfiMemoryMapSize
);
209 ASSERT (EfiMemoryMap
!= NULL
);
210 Status
= gBS
->GetMemoryMap (
215 &EfiDescriptorVersion
217 if (EFI_ERROR (Status
)) {
218 FreePool (EfiMemoryMap
);
220 } while (Status
== EFI_BUFFER_TOO_SMALL
);
222 ASSERT_EFI_ERROR (Status
);
225 // Sort memory map from low to high
227 EfiEntry
= EfiMemoryMap
;
228 NextEfiEntry
= NEXT_MEMORY_DESCRIPTOR (EfiEntry
, EfiDescriptorSize
);
229 EfiMemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) EfiMemoryMap
+ EfiMemoryMapSize
);
230 while (EfiEntry
< EfiMemoryMapEnd
) {
231 while (NextEfiEntry
< EfiMemoryMapEnd
) {
232 if (EfiEntry
->PhysicalStart
> NextEfiEntry
->PhysicalStart
) {
233 CopyMem (&TempEfiEntry
, EfiEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
234 CopyMem (EfiEntry
, NextEfiEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
235 CopyMem (NextEfiEntry
, &TempEfiEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
238 NextEfiEntry
= NEXT_MEMORY_DESCRIPTOR (NextEfiEntry
, EfiDescriptorSize
);
241 EfiEntry
= NEXT_MEMORY_DESCRIPTOR (EfiEntry
, EfiDescriptorSize
);
242 NextEfiEntry
= NEXT_MEMORY_DESCRIPTOR (EfiEntry
, EfiDescriptorSize
);
248 DEBUG ((DEBUG_INFO
, "MemoryMap:\n"));
249 EfiEntry
= EfiMemoryMap
;
250 EfiMemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) EfiMemoryMap
+ EfiMemoryMapSize
);
251 while (EfiEntry
< EfiMemoryMapEnd
) {
252 MemoryBlockLength
= (UINT64
) (LShiftU64 (EfiEntry
->NumberOfPages
, 12));
253 DEBUG ((DEBUG_INFO
, "Entry(0x%02x) 0x%016lx - 0x%016lx\n", EfiEntry
->Type
, EfiEntry
->PhysicalStart
, EfiEntry
->PhysicalStart
+ MemoryBlockLength
));
254 switch (EfiEntry
->Type
) {
257 case EfiBootServicesCode
:
258 case EfiBootServicesData
:
259 case EfiConventionalMemory
:
260 case EfiRuntimeServicesCode
:
261 case EfiRuntimeServicesData
:
262 case EfiACPIReclaimMemory
:
263 case EfiACPIMemoryNVS
:
264 case EfiReservedMemoryType
:
265 if ((EfiEntry
->PhysicalStart
+ MemoryBlockLength
) <= BASE_1MB
) {
267 // Skip the memory block is under 1MB
269 } else if (EfiEntry
->PhysicalStart
>= BASE_4GB
) {
270 if (*Above4GMemoryLimit
< EfiEntry
->PhysicalStart
+ MemoryBlockLength
) {
271 *Above4GMemoryLimit
= EfiEntry
->PhysicalStart
+ MemoryBlockLength
;
274 if (*Below4GMemoryLimit
< EfiEntry
->PhysicalStart
+ MemoryBlockLength
) {
275 *Below4GMemoryLimit
= EfiEntry
->PhysicalStart
+ MemoryBlockLength
;
280 EfiEntry
= NEXT_MEMORY_DESCRIPTOR (EfiEntry
, EfiDescriptorSize
);
283 FreePool (EfiMemoryMap
);
285 DEBUG ((DEBUG_INFO
, "Result:\n"));
286 DEBUG ((DEBUG_INFO
, "Below4GMemoryLimit: 0x%016lx\n", *Below4GMemoryLimit
));
287 DEBUG ((DEBUG_INFO
, "Above4GMemoryLimit: 0x%016lx\n", *Above4GMemoryLimit
));
293 The scan bus callback function to always enable page attribute.
295 @param[in] Context The context of the callback.
296 @param[in] Segment The segment of the source.
297 @param[in] Bus The bus of the source.
298 @param[in] Device The device of the source.
299 @param[in] Function The function of the source.
301 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device.
305 ScanBusCallbackAlwaysEnablePageAttribute (
313 VTD_SOURCE_ID SourceId
;
316 SourceId
.Bits
.Bus
= Bus
;
317 SourceId
.Bits
.Device
= Device
;
318 SourceId
.Bits
.Function
= Function
;
319 Status
= AlwaysEnablePageAttribute (Segment
, SourceId
);
324 Always enable the VTd page attribute for the device in the DeviceScope.
326 @param[in] DeviceScope the input device scope data structure
328 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device in the device scope.
331 AlwaysEnablePageAttributeDeviceScope (
332 IN EDKII_PLATFORM_VTD_DEVICE_SCOPE
*DeviceScope
338 VTD_SOURCE_ID SourceId
;
339 UINT8 SecondaryBusNumber
;
342 Status
= GetPciBusDeviceFunction (DeviceScope
->SegmentNumber
, &DeviceScope
->DeviceScope
, &Bus
, &Device
, &Function
);
344 if (DeviceScope
->DeviceScope
.Type
== EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE
) {
346 // Need scan the bridge and add all devices.
348 SecondaryBusNumber
= PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(DeviceScope
->SegmentNumber
, Bus
, Device
, Function
, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET
));
349 Status
= ScanPciBus (NULL
, DeviceScope
->SegmentNumber
, SecondaryBusNumber
, ScanBusCallbackAlwaysEnablePageAttribute
);
352 SourceId
.Bits
.Bus
= Bus
;
353 SourceId
.Bits
.Device
= Device
;
354 SourceId
.Bits
.Function
= Function
;
355 Status
= AlwaysEnablePageAttribute (DeviceScope
->SegmentNumber
, SourceId
);
361 Always enable the VTd page attribute for the device matching DeviceId.
363 @param[in] PciDeviceId the input PCI device ID
365 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device matching DeviceId.
368 AlwaysEnablePageAttributePciDeviceId (
369 IN EDKII_PLATFORM_VTD_PCI_DEVICE_ID
*PciDeviceId
374 PCI_DEVICE_DATA
*PciDeviceData
;
377 for (VtdIndex
= 0; VtdIndex
< mVtdUnitNumber
; VtdIndex
++) {
378 for (PciIndex
= 0; PciIndex
< mVtdUnitInformation
[VtdIndex
].PciDeviceInfo
.PciDeviceDataNumber
; PciIndex
++) {
379 PciDeviceData
= &mVtdUnitInformation
[VtdIndex
].PciDeviceInfo
.PciDeviceData
[PciIndex
];
381 if (((PciDeviceId
->VendorId
== 0xFFFF) || (PciDeviceId
->VendorId
== PciDeviceData
->PciDeviceId
.VendorId
)) &&
382 ((PciDeviceId
->DeviceId
== 0xFFFF) || (PciDeviceId
->DeviceId
== PciDeviceData
->PciDeviceId
.DeviceId
)) &&
383 ((PciDeviceId
->RevisionId
== 0xFF) || (PciDeviceId
->RevisionId
== PciDeviceData
->PciDeviceId
.RevisionId
)) &&
384 ((PciDeviceId
->SubsystemVendorId
== 0xFFFF) || (PciDeviceId
->SubsystemVendorId
== PciDeviceData
->PciDeviceId
.SubsystemVendorId
)) &&
385 ((PciDeviceId
->SubsystemDeviceId
== 0xFFFF) || (PciDeviceId
->SubsystemDeviceId
== PciDeviceData
->PciDeviceId
.SubsystemDeviceId
)) ) {
386 Status
= AlwaysEnablePageAttribute (mVtdUnitInformation
[VtdIndex
].Segment
, PciDeviceData
->PciSourceId
);
387 if (EFI_ERROR(Status
)) {
397 Always enable the VTd page attribute for the device.
399 @param[in] DeviceInfo the exception device information
401 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device in the device info.
404 AlwaysEnablePageAttributeExceptionDeviceInfo (
405 IN EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO
*DeviceInfo
408 switch (DeviceInfo
->Type
) {
409 case EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_DEVICE_SCOPE
:
410 return AlwaysEnablePageAttributeDeviceScope ((VOID
*)(DeviceInfo
+ 1));
411 case EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_PCI_DEVICE_ID
:
412 return AlwaysEnablePageAttributePciDeviceId ((VOID
*)(DeviceInfo
+ 1));
414 return EFI_UNSUPPORTED
;
419 Initialize platform VTd policy.
422 InitializePlatformVTdPolicy (
427 UINTN DeviceInfoCount
;
429 EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO
*ThisDeviceInfo
;
435 Status
= gBS
->LocateProtocol (
436 &gEdkiiPlatformVTdPolicyProtocolGuid
,
438 (VOID
**)&mPlatformVTdPolicy
440 if (!EFI_ERROR(Status
)) {
441 DEBUG ((DEBUG_INFO
, "InitializePlatformVTdPolicy\n"));
442 Status
= mPlatformVTdPolicy
->GetExceptionDeviceList (mPlatformVTdPolicy
, &DeviceInfoCount
, &DeviceInfo
);
443 if (!EFI_ERROR(Status
)) {
444 ThisDeviceInfo
= DeviceInfo
;
445 for (Index
= 0; Index
< DeviceInfoCount
; Index
++) {
446 if (ThisDeviceInfo
->Type
== EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_END
) {
449 AlwaysEnablePageAttributeExceptionDeviceInfo (ThisDeviceInfo
);
450 ThisDeviceInfo
= (VOID
*)((UINTN
)ThisDeviceInfo
+ ThisDeviceInfo
->Length
);
452 FreePool (DeviceInfo
);
466 VOID
*PciEnumerationComplete
;
468 UINT64 Below4GMemoryLimit
;
469 UINT64 Above4GMemoryLimit
;
472 // PCI Enumeration must be done
474 Status
= gBS
->LocateProtocol (
475 &gEfiPciEnumerationCompleteProtocolGuid
,
477 &PciEnumerationComplete
479 ASSERT_EFI_ERROR (Status
);
481 ReturnUefiMemoryMap (&Below4GMemoryLimit
, &Above4GMemoryLimit
);
482 Below4GMemoryLimit
= ALIGN_VALUE_UP(Below4GMemoryLimit
, SIZE_256MB
);
483 DEBUG ((DEBUG_INFO
, " Adjusted Below4GMemoryLimit: 0x%016lx\n", Below4GMemoryLimit
));
485 mBelow4GMemoryLimit
= Below4GMemoryLimit
;
486 mAbove4GMemoryLimit
= Above4GMemoryLimit
;
491 DEBUG ((DEBUG_INFO
, "ParseDmarAcpiTable\n"));
492 Status
= ParseDmarAcpiTableDrhd ();
493 if (EFI_ERROR (Status
)) {
496 DEBUG ((DEBUG_INFO
, "PrepareVtdConfig\n"));
502 DEBUG ((DEBUG_INFO
, "SetupTranslationTable\n"));
503 Status
= SetupTranslationTable ();
504 if (EFI_ERROR (Status
)) {
508 InitializePlatformVTdPolicy ();
510 ParseDmarAcpiTableRmrr ();
512 if ((PcdGet8 (PcdVTdPolicyPropertyMask
) & BIT2
) == 0) {
514 // Support IOMMU access attribute request recording before DMAR table is installed.
515 // Here is to process the requests.
517 ProcessRequestedAccessAttribute ();
520 for (Index
= 0; Index
< mVtdUnitNumber
; Index
++) {
521 DEBUG ((DEBUG_INFO
,"VTD Unit %d (Segment: %04x)\n", Index
, mVtdUnitInformation
[Index
].Segment
));
522 if (mVtdUnitInformation
[Index
].ExtRootEntryTable
!= NULL
) {
523 DumpDmarExtContextEntryTable (mVtdUnitInformation
[Index
].ExtRootEntryTable
);
525 if (mVtdUnitInformation
[Index
].RootEntryTable
!= NULL
) {
526 DumpDmarContextEntryTable (mVtdUnitInformation
[Index
].RootEntryTable
);
533 DEBUG ((DEBUG_INFO
, "EnableDmar\n"));
534 Status
= EnableDmar ();
535 if (EFI_ERROR (Status
)) {
538 DEBUG ((DEBUG_INFO
, "DumpVtdRegs\n"));
543 Notification function of ACPI Table change.
545 This is a notification function registered on ACPI Table change event.
547 @param Event Event whose notification function is being invoked.
548 @param Context Pointer to the notification function's context.
553 AcpiNotificationFunc (
560 Status
= GetDmarAcpiTable ();
561 if (EFI_ERROR (Status
)) {
562 if (Status
== EFI_ALREADY_STARTED
) {
563 gBS
->CloseEvent (Event
);
568 gBS
->CloseEvent (Event
);
572 Exit boot service callback function.
574 @param[in] Event The event handle.
575 @param[in] Context The event content.
586 DEBUG ((DEBUG_INFO
, "Vtd OnExitBootServices\n"));
589 DEBUG ((DEBUG_INFO
, "Invalidate all\n"));
590 for (VtdIndex
= 0; VtdIndex
< mVtdUnitNumber
; VtdIndex
++) {
591 FlushWriteBuffer (VtdIndex
);
593 InvalidateContextCache (VtdIndex
);
595 InvalidateIOTLB (VtdIndex
);
598 if ((PcdGet8(PcdVTdPolicyPropertyMask
) & BIT1
) == 0) {
605 Legacy boot callback function.
607 @param[in] Event The event handle.
608 @param[in] Context The event content.
617 DEBUG ((DEBUG_INFO
, "Vtd OnLegacyBoot\n"));
624 Initialize DMA protection.
627 InitializeDmaProtection (
632 EFI_EVENT ExitBootServicesEvent
;
633 EFI_EVENT LegacyBootEvent
;
634 EFI_EVENT EventAcpi10
;
635 EFI_EVENT EventAcpi20
;
637 Status
= gBS
->CreateEventEx (
640 AcpiNotificationFunc
,
642 &gEfiAcpi10TableGuid
,
645 ASSERT_EFI_ERROR (Status
);
647 Status
= gBS
->CreateEventEx (
650 AcpiNotificationFunc
,
652 &gEfiAcpi20TableGuid
,
655 ASSERT_EFI_ERROR (Status
);
658 // Signal the events initially for the case
659 // that DMAR table has been installed.
661 gBS
->SignalEvent (EventAcpi20
);
662 gBS
->SignalEvent (EventAcpi10
);
664 Status
= gBS
->CreateEventEx (
669 &gEfiEventExitBootServicesGuid
,
670 &ExitBootServicesEvent
672 ASSERT_EFI_ERROR (Status
);
674 Status
= EfiCreateEventLegacyBootEx (
680 ASSERT_EFI_ERROR (Status
);