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.
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.
14 #include "DmaProtection.h"
16 UINT64 mBelow4GMemoryLimit
;
17 UINT64 mAbove4GMemoryLimit
;
19 EDKII_PLATFORM_VTD_POLICY_PROTOCOL
*mPlatformVTdPolicy
;
21 VTD_ACCESS_REQUEST
*mAccessRequest
= NULL
;
22 UINTN mAccessRequestCount
= 0;
23 UINTN mAccessRequestMaxCount
= 0;
26 Append VTd Access Request to global.
28 @param[in] Segment The Segment used to identify a VTd engine.
29 @param[in] SourceId The SourceId used to identify a VTd engine and table entry.
30 @param[in] BaseAddress The base of device memory address to be used as the DMA memory.
31 @param[in] Length The length of device memory address to be used as the DMA memory.
32 @param[in] IoMmuAccess The IOMMU access.
34 @retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by BaseAddress and Length.
35 @retval EFI_INVALID_PARAMETER BaseAddress is not IoMmu Page size aligned.
36 @retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned.
37 @retval EFI_INVALID_PARAMETER Length is 0.
38 @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access.
39 @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU.
40 @retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by BaseAddress and Length.
41 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access.
42 @retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation.
46 RequestAccessAttribute (
48 IN VTD_SOURCE_ID SourceId
,
49 IN UINT64 BaseAddress
,
54 VTD_ACCESS_REQUEST
*NewAccessRequest
;
58 // Optimization for memory.
60 // If the last record is to IoMmuAccess=0,
61 // Check previous records and remove the matched entry.
63 if (IoMmuAccess
== 0) {
64 for (Index
= 0; Index
< mAccessRequestCount
; Index
++) {
65 if ((mAccessRequest
[Index
].Segment
== Segment
) &&
66 (mAccessRequest
[Index
].SourceId
.Uint16
== SourceId
.Uint16
) &&
67 (mAccessRequest
[Index
].BaseAddress
== BaseAddress
) &&
68 (mAccessRequest
[Index
].Length
== Length
) &&
69 (mAccessRequest
[Index
].IoMmuAccess
!= 0)) {
71 // Remove this record [Index].
72 // No need to add the new record.
74 if (Index
!= mAccessRequestCount
- 1) {
76 &mAccessRequest
[Index
],
77 &mAccessRequest
[Index
+ 1],
78 sizeof (VTD_ACCESS_REQUEST
) * (mAccessRequestCount
- 1 - Index
)
81 ZeroMem (&mAccessRequest
[mAccessRequestCount
- 1], sizeof(VTD_ACCESS_REQUEST
));
82 mAccessRequestCount
--;
88 if (mAccessRequestCount
>= mAccessRequestMaxCount
) {
89 NewAccessRequest
= AllocateZeroPool (sizeof(*NewAccessRequest
) * (mAccessRequestMaxCount
+ MAX_VTD_ACCESS_REQUEST
));
90 if (NewAccessRequest
== NULL
) {
91 return EFI_OUT_OF_RESOURCES
;
93 mAccessRequestMaxCount
+= MAX_VTD_ACCESS_REQUEST
;
94 if (mAccessRequest
!= NULL
) {
95 CopyMem (NewAccessRequest
, mAccessRequest
, sizeof(*NewAccessRequest
) * mAccessRequestCount
);
96 FreePool (mAccessRequest
);
98 mAccessRequest
= NewAccessRequest
;
101 ASSERT (mAccessRequestCount
< mAccessRequestMaxCount
);
103 mAccessRequest
[mAccessRequestCount
].Segment
= Segment
;
104 mAccessRequest
[mAccessRequestCount
].SourceId
= SourceId
;
105 mAccessRequest
[mAccessRequestCount
].BaseAddress
= BaseAddress
;
106 mAccessRequest
[mAccessRequestCount
].Length
= Length
;
107 mAccessRequest
[mAccessRequestCount
].IoMmuAccess
= IoMmuAccess
;
109 mAccessRequestCount
++;
115 Process Access Requests from before DMAR table is installed.
119 ProcessRequestedAccessAttribute (
126 DEBUG ((DEBUG_INFO
, "ProcessRequestedAccessAttribute ...\n"));
128 for (Index
= 0; Index
< mAccessRequestCount
; Index
++) {
131 "PCI(S%x.B%x.D%x.F%x) ",
132 mAccessRequest
[Index
].Segment
,
133 mAccessRequest
[Index
].SourceId
.Bits
.Bus
,
134 mAccessRequest
[Index
].SourceId
.Bits
.Device
,
135 mAccessRequest
[Index
].SourceId
.Bits
.Function
139 "(0x%lx~0x%lx) - %lx\n",
140 mAccessRequest
[Index
].BaseAddress
,
141 mAccessRequest
[Index
].Length
,
142 mAccessRequest
[Index
].IoMmuAccess
144 Status
= SetAccessAttribute (
145 mAccessRequest
[Index
].Segment
,
146 mAccessRequest
[Index
].SourceId
,
147 mAccessRequest
[Index
].BaseAddress
,
148 mAccessRequest
[Index
].Length
,
149 mAccessRequest
[Index
].IoMmuAccess
151 if (EFI_ERROR (Status
)) {
152 DEBUG ((DEBUG_ERROR
, "SetAccessAttribute %r: ", Status
));
156 if (mAccessRequest
!= NULL
) {
157 FreePool (mAccessRequest
);
159 mAccessRequest
= NULL
;
160 mAccessRequestCount
= 0;
161 mAccessRequestMaxCount
= 0;
163 DEBUG ((DEBUG_INFO
, "ProcessRequestedAccessAttribute Done\n"));
167 return the UEFI memory information.
169 @param[out] Below4GMemoryLimit The below 4GiB memory limit
170 @param[out] Above4GMemoryLimit The above 4GiB memory limit
173 ReturnUefiMemoryMap (
174 OUT UINT64
*Below4GMemoryLimit
,
175 OUT UINT64
*Above4GMemoryLimit
179 EFI_MEMORY_DESCRIPTOR
*EfiMemoryMap
;
180 EFI_MEMORY_DESCRIPTOR
*EfiMemoryMapEnd
;
181 EFI_MEMORY_DESCRIPTOR
*EfiEntry
;
182 EFI_MEMORY_DESCRIPTOR
*NextEfiEntry
;
183 EFI_MEMORY_DESCRIPTOR TempEfiEntry
;
184 UINTN EfiMemoryMapSize
;
186 UINTN EfiDescriptorSize
;
187 UINT32 EfiDescriptorVersion
;
188 UINT64 MemoryBlockLength
;
190 *Below4GMemoryLimit
= 0;
191 *Above4GMemoryLimit
= 0;
194 // Get the EFI memory map.
196 EfiMemoryMapSize
= 0;
198 Status
= gBS
->GetMemoryMap (
203 &EfiDescriptorVersion
205 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
209 // Use size returned back plus 1 descriptor for the AllocatePool.
210 // We don't just multiply by 2 since the "for" loop below terminates on
211 // EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwize
212 // we process bogus entries and create bogus E820 entries.
214 EfiMemoryMap
= (EFI_MEMORY_DESCRIPTOR
*) AllocatePool (EfiMemoryMapSize
);
215 ASSERT (EfiMemoryMap
!= NULL
);
216 Status
= gBS
->GetMemoryMap (
221 &EfiDescriptorVersion
223 if (EFI_ERROR (Status
)) {
224 FreePool (EfiMemoryMap
);
226 } while (Status
== EFI_BUFFER_TOO_SMALL
);
228 ASSERT_EFI_ERROR (Status
);
231 // Sort memory map from low to high
233 EfiEntry
= EfiMemoryMap
;
234 NextEfiEntry
= NEXT_MEMORY_DESCRIPTOR (EfiEntry
, EfiDescriptorSize
);
235 EfiMemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) EfiMemoryMap
+ EfiMemoryMapSize
);
236 while (EfiEntry
< EfiMemoryMapEnd
) {
237 while (NextEfiEntry
< EfiMemoryMapEnd
) {
238 if (EfiEntry
->PhysicalStart
> NextEfiEntry
->PhysicalStart
) {
239 CopyMem (&TempEfiEntry
, EfiEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
240 CopyMem (EfiEntry
, NextEfiEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
241 CopyMem (NextEfiEntry
, &TempEfiEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
244 NextEfiEntry
= NEXT_MEMORY_DESCRIPTOR (NextEfiEntry
, EfiDescriptorSize
);
247 EfiEntry
= NEXT_MEMORY_DESCRIPTOR (EfiEntry
, EfiDescriptorSize
);
248 NextEfiEntry
= NEXT_MEMORY_DESCRIPTOR (EfiEntry
, EfiDescriptorSize
);
254 DEBUG ((DEBUG_INFO
, "MemoryMap:\n"));
255 EfiEntry
= EfiMemoryMap
;
256 EfiMemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*) ((UINT8
*) EfiMemoryMap
+ EfiMemoryMapSize
);
257 while (EfiEntry
< EfiMemoryMapEnd
) {
258 MemoryBlockLength
= (UINT64
) (LShiftU64 (EfiEntry
->NumberOfPages
, 12));
259 DEBUG ((DEBUG_INFO
, "Entry(0x%02x) 0x%016lx - 0x%016lx\n", EfiEntry
->Type
, EfiEntry
->PhysicalStart
, EfiEntry
->PhysicalStart
+ MemoryBlockLength
));
260 switch (EfiEntry
->Type
) {
263 case EfiBootServicesCode
:
264 case EfiBootServicesData
:
265 case EfiConventionalMemory
:
266 case EfiRuntimeServicesCode
:
267 case EfiRuntimeServicesData
:
268 case EfiACPIReclaimMemory
:
269 case EfiACPIMemoryNVS
:
270 case EfiReservedMemoryType
:
271 if ((EfiEntry
->PhysicalStart
+ MemoryBlockLength
) <= BASE_1MB
) {
273 // Skip the memory block is under 1MB
275 } else if (EfiEntry
->PhysicalStart
>= BASE_4GB
) {
276 if (*Above4GMemoryLimit
< EfiEntry
->PhysicalStart
+ MemoryBlockLength
) {
277 *Above4GMemoryLimit
= EfiEntry
->PhysicalStart
+ MemoryBlockLength
;
280 if (*Below4GMemoryLimit
< EfiEntry
->PhysicalStart
+ MemoryBlockLength
) {
281 *Below4GMemoryLimit
= EfiEntry
->PhysicalStart
+ MemoryBlockLength
;
286 EfiEntry
= NEXT_MEMORY_DESCRIPTOR (EfiEntry
, EfiDescriptorSize
);
289 FreePool (EfiMemoryMap
);
291 DEBUG ((DEBUG_INFO
, "Result:\n"));
292 DEBUG ((DEBUG_INFO
, "Below4GMemoryLimit: 0x%016lx\n", *Below4GMemoryLimit
));
293 DEBUG ((DEBUG_INFO
, "Above4GMemoryLimit: 0x%016lx\n", *Above4GMemoryLimit
));
299 The scan bus callback function to always enable page attribute.
301 @param[in] Context The context of the callback.
302 @param[in] Segment The segment of the source.
303 @param[in] Bus The bus of the source.
304 @param[in] Device The device of the source.
305 @param[in] Function The function of the source.
307 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device.
311 ScanBusCallbackAlwaysEnablePageAttribute (
319 VTD_SOURCE_ID SourceId
;
322 SourceId
.Bits
.Bus
= Bus
;
323 SourceId
.Bits
.Device
= Device
;
324 SourceId
.Bits
.Function
= Function
;
325 Status
= AlwaysEnablePageAttribute (Segment
, SourceId
);
330 Always enable the VTd page attribute for the device in the DeviceScope.
332 @param[in] DeviceScope the input device scope data structure
334 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device in the device scope.
337 AlwaysEnablePageAttributeDeviceScope (
338 IN EDKII_PLATFORM_VTD_DEVICE_SCOPE
*DeviceScope
344 VTD_SOURCE_ID SourceId
;
345 UINT8 SecondaryBusNumber
;
348 Status
= GetPciBusDeviceFunction (DeviceScope
->SegmentNumber
, &DeviceScope
->DeviceScope
, &Bus
, &Device
, &Function
);
350 if (DeviceScope
->DeviceScope
.Type
== EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE
) {
352 // Need scan the bridge and add all devices.
354 SecondaryBusNumber
= PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(DeviceScope
->SegmentNumber
, Bus
, Device
, Function
, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET
));
355 Status
= ScanPciBus (NULL
, DeviceScope
->SegmentNumber
, SecondaryBusNumber
, ScanBusCallbackAlwaysEnablePageAttribute
);
358 SourceId
.Bits
.Bus
= Bus
;
359 SourceId
.Bits
.Device
= Device
;
360 SourceId
.Bits
.Function
= Function
;
361 Status
= AlwaysEnablePageAttribute (DeviceScope
->SegmentNumber
, SourceId
);
367 Always enable the VTd page attribute for the device matching DeviceId.
369 @param[in] PciDeviceId the input PCI device ID
371 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device matching DeviceId.
374 AlwaysEnablePageAttributePciDeviceId (
375 IN EDKII_PLATFORM_VTD_PCI_DEVICE_ID
*PciDeviceId
380 PCI_DEVICE_DATA
*PciDeviceData
;
383 for (VtdIndex
= 0; VtdIndex
< mVtdUnitNumber
; VtdIndex
++) {
384 for (PciIndex
= 0; PciIndex
< mVtdUnitInformation
[VtdIndex
].PciDeviceInfo
.PciDeviceDataNumber
; PciIndex
++) {
385 PciDeviceData
= &mVtdUnitInformation
[VtdIndex
].PciDeviceInfo
.PciDeviceData
[PciIndex
];
387 if (((PciDeviceId
->VendorId
== 0xFFFF) || (PciDeviceId
->VendorId
== PciDeviceData
->PciDeviceId
.VendorId
)) &&
388 ((PciDeviceId
->DeviceId
== 0xFFFF) || (PciDeviceId
->DeviceId
== PciDeviceData
->PciDeviceId
.DeviceId
)) &&
389 ((PciDeviceId
->RevisionId
== 0xFF) || (PciDeviceId
->RevisionId
== PciDeviceData
->PciDeviceId
.RevisionId
)) &&
390 ((PciDeviceId
->SubsystemVendorId
== 0xFFFF) || (PciDeviceId
->SubsystemVendorId
== PciDeviceData
->PciDeviceId
.SubsystemVendorId
)) &&
391 ((PciDeviceId
->SubsystemDeviceId
== 0xFFFF) || (PciDeviceId
->SubsystemDeviceId
== PciDeviceData
->PciDeviceId
.SubsystemDeviceId
)) ) {
392 Status
= AlwaysEnablePageAttribute (mVtdUnitInformation
[VtdIndex
].Segment
, PciDeviceData
->PciSourceId
);
393 if (EFI_ERROR(Status
)) {
403 Always enable the VTd page attribute for the device.
405 @param[in] DeviceInfo the exception device information
407 @retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device in the device info.
410 AlwaysEnablePageAttributeExceptionDeviceInfo (
411 IN EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO
*DeviceInfo
414 switch (DeviceInfo
->Type
) {
415 case EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_DEVICE_SCOPE
:
416 return AlwaysEnablePageAttributeDeviceScope ((VOID
*)(DeviceInfo
+ 1));
417 case EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_PCI_DEVICE_ID
:
418 return AlwaysEnablePageAttributePciDeviceId ((VOID
*)(DeviceInfo
+ 1));
420 return EFI_UNSUPPORTED
;
425 Initialize platform VTd policy.
428 InitializePlatformVTdPolicy (
433 UINTN DeviceInfoCount
;
435 EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO
*ThisDeviceInfo
;
441 Status
= gBS
->LocateProtocol (
442 &gEdkiiPlatformVTdPolicyProtocolGuid
,
444 (VOID
**)&mPlatformVTdPolicy
446 if (!EFI_ERROR(Status
)) {
447 DEBUG ((DEBUG_INFO
, "InitializePlatformVTdPolicy\n"));
448 Status
= mPlatformVTdPolicy
->GetExceptionDeviceList (mPlatformVTdPolicy
, &DeviceInfoCount
, &DeviceInfo
);
449 if (!EFI_ERROR(Status
)) {
450 ThisDeviceInfo
= DeviceInfo
;
451 for (Index
= 0; Index
< DeviceInfoCount
; Index
++) {
452 if (ThisDeviceInfo
->Type
== EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_END
) {
455 AlwaysEnablePageAttributeExceptionDeviceInfo (ThisDeviceInfo
);
456 ThisDeviceInfo
= (VOID
*)((UINTN
)ThisDeviceInfo
+ ThisDeviceInfo
->Length
);
458 FreePool (DeviceInfo
);
472 VOID
*PciEnumerationComplete
;
474 UINT64 Below4GMemoryLimit
;
475 UINT64 Above4GMemoryLimit
;
478 // PCI Enumeration must be done
480 Status
= gBS
->LocateProtocol (
481 &gEfiPciEnumerationCompleteProtocolGuid
,
483 &PciEnumerationComplete
485 ASSERT_EFI_ERROR (Status
);
487 ReturnUefiMemoryMap (&Below4GMemoryLimit
, &Above4GMemoryLimit
);
488 Below4GMemoryLimit
= ALIGN_VALUE_UP(Below4GMemoryLimit
, SIZE_256MB
);
489 DEBUG ((DEBUG_INFO
, " Adjusted Below4GMemoryLimit: 0x%016lx\n", Below4GMemoryLimit
));
491 mBelow4GMemoryLimit
= Below4GMemoryLimit
;
492 mAbove4GMemoryLimit
= Above4GMemoryLimit
;
497 DEBUG ((DEBUG_INFO
, "ParseDmarAcpiTable\n"));
498 Status
= ParseDmarAcpiTableDrhd ();
499 if (EFI_ERROR (Status
)) {
502 DEBUG ((DEBUG_INFO
, "PrepareVtdConfig\n"));
508 DEBUG ((DEBUG_INFO
, "SetupTranslationTable\n"));
509 Status
= SetupTranslationTable ();
510 if (EFI_ERROR (Status
)) {
514 InitializePlatformVTdPolicy ();
516 ParseDmarAcpiTableRmrr ();
518 if ((PcdGet8 (PcdVTdPolicyPropertyMask
) & BIT2
) == 0) {
520 // Support IOMMU access attribute request recording before DMAR table is installed.
521 // Here is to process the requests.
523 ProcessRequestedAccessAttribute ();
526 for (Index
= 0; Index
< mVtdUnitNumber
; Index
++) {
527 DEBUG ((DEBUG_INFO
,"VTD Unit %d (Segment: %04x)\n", Index
, mVtdUnitInformation
[Index
].Segment
));
528 if (mVtdUnitInformation
[Index
].ExtRootEntryTable
!= NULL
) {
529 DumpDmarExtContextEntryTable (mVtdUnitInformation
[Index
].ExtRootEntryTable
);
531 if (mVtdUnitInformation
[Index
].RootEntryTable
!= NULL
) {
532 DumpDmarContextEntryTable (mVtdUnitInformation
[Index
].RootEntryTable
);
539 DEBUG ((DEBUG_INFO
, "EnableDmar\n"));
540 Status
= EnableDmar ();
541 if (EFI_ERROR (Status
)) {
544 DEBUG ((DEBUG_INFO
, "DumpVtdRegs\n"));
549 Notification function of ACPI Table change.
551 This is a notification function registered on ACPI Table change event.
553 @param Event Event whose notification function is being invoked.
554 @param Context Pointer to the notification function's context.
559 AcpiNotificationFunc (
566 Status
= GetDmarAcpiTable ();
567 if (EFI_ERROR (Status
)) {
568 if (Status
== EFI_ALREADY_STARTED
) {
569 gBS
->CloseEvent (Event
);
574 gBS
->CloseEvent (Event
);
578 Exit boot service callback function.
580 @param[in] Event The event handle.
581 @param[in] Context The event content.
590 DEBUG ((DEBUG_INFO
, "Vtd OnExitBootServices\n"));
593 if ((PcdGet8(PcdVTdPolicyPropertyMask
) & BIT1
) == 0) {
600 Legacy boot callback function.
602 @param[in] Event The event handle.
603 @param[in] Context The event content.
612 DEBUG ((DEBUG_INFO
, "Vtd OnLegacyBoot\n"));
619 Initialize DMA protection.
622 InitializeDmaProtection (
627 EFI_EVENT ExitBootServicesEvent
;
628 EFI_EVENT LegacyBootEvent
;
629 EFI_EVENT EventAcpi10
;
630 EFI_EVENT EventAcpi20
;
632 Status
= gBS
->CreateEventEx (
635 AcpiNotificationFunc
,
637 &gEfiAcpi10TableGuid
,
640 ASSERT_EFI_ERROR (Status
);
642 Status
= gBS
->CreateEventEx (
645 AcpiNotificationFunc
,
647 &gEfiAcpi20TableGuid
,
650 ASSERT_EFI_ERROR (Status
);
653 // Signal the events initially for the case
654 // that DMAR table has been installed.
656 gBS
->SignalEvent (EventAcpi20
);
657 gBS
->SignalEvent (EventAcpi10
);
659 Status
= gBS
->CreateEventEx (
664 &gEfiEventExitBootServicesGuid
,
665 &ExitBootServicesEvent
667 ASSERT_EFI_ERROR (Status
);
669 Status
= EfiCreateEventLegacyBootEx (
675 ASSERT_EFI_ERROR (Status
);