1 /** @file
2   The DMA memory help function.
3 
4   Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "SdBlockIoPei.h"
11 
12 EDKII_IOMMU_PPI  *mIoMmu;
13 
14 /**
15   Provides the controller-specific addresses required to access system memory from a
16   DMA bus master.
17 
18   @param  Operation             Indicates if the bus master is going to read or write to system memory.
19   @param  HostAddress           The system memory address to map to the PCI controller.
20   @param  NumberOfBytes         On input the number of bytes to map. On output the number of bytes
21                                 that were mapped.
22   @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
23                                 access the hosts HostAddress.
24   @param  Mapping               A resulting value to pass to Unmap().
25 
26   @retval EFI_SUCCESS           The range was mapped for the returned NumberOfBytes.
27   @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common buffer.
28   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
29   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
30   @retval EFI_DEVICE_ERROR      The system hardware could not map the requested address.
31 
32 **/
33 EFI_STATUS
IoMmuMap(IN EDKII_IOMMU_OPERATION Operation,IN VOID * HostAddress,IN OUT UINTN * NumberOfBytes,OUT EFI_PHYSICAL_ADDRESS * DeviceAddress,OUT VOID ** Mapping)34 IoMmuMap (
35   IN  EDKII_IOMMU_OPERATION Operation,
36   IN VOID                   *HostAddress,
37   IN  OUT UINTN             *NumberOfBytes,
38   OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
39   OUT VOID                  **Mapping
40   )
41 {
42   EFI_STATUS  Status;
43   UINT64      Attribute;
44 
45   if (mIoMmu != NULL) {
46     Status = mIoMmu->Map (
47                        mIoMmu,
48                        Operation,
49                        HostAddress,
50                        NumberOfBytes,
51                        DeviceAddress,
52                        Mapping
53                        );
54     if (EFI_ERROR (Status)) {
55       return EFI_OUT_OF_RESOURCES;
56     }
57     switch (Operation) {
58     case EdkiiIoMmuOperationBusMasterRead:
59     case EdkiiIoMmuOperationBusMasterRead64:
60       Attribute = EDKII_IOMMU_ACCESS_READ;
61       break;
62     case EdkiiIoMmuOperationBusMasterWrite:
63     case EdkiiIoMmuOperationBusMasterWrite64:
64       Attribute = EDKII_IOMMU_ACCESS_WRITE;
65       break;
66     case EdkiiIoMmuOperationBusMasterCommonBuffer:
67     case EdkiiIoMmuOperationBusMasterCommonBuffer64:
68       Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE;
69       break;
70     default:
71       ASSERT(FALSE);
72       return EFI_INVALID_PARAMETER;
73     }
74     Status = mIoMmu->SetAttribute (
75                        mIoMmu,
76                        *Mapping,
77                        Attribute
78                        );
79     if (EFI_ERROR (Status)) {
80       return Status;
81     }
82   } else {
83     *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;
84     *Mapping = NULL;
85     Status = EFI_SUCCESS;
86   }
87   return Status;
88 }
89 
90 /**
91   Completes the Map() operation and releases any corresponding resources.
92 
93   @param  Mapping               The mapping value returned from Map().
94 
95   @retval EFI_SUCCESS           The range was unmapped.
96   @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
97   @retval EFI_DEVICE_ERROR      The data was not committed to the target system memory.
98 **/
99 EFI_STATUS
IoMmuUnmap(IN VOID * Mapping)100 IoMmuUnmap (
101   IN VOID                  *Mapping
102   )
103 {
104   EFI_STATUS  Status;
105 
106   if (mIoMmu != NULL) {
107     Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0);
108     Status = mIoMmu->Unmap (mIoMmu, Mapping);
109   } else {
110     Status = EFI_SUCCESS;
111   }
112   return Status;
113 }
114 
115 /**
116   Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
117   OperationBusMasterCommonBuffer64 mapping.
118 
119   @param  Pages                 The number of pages to allocate.
120   @param  HostAddress           A pointer to store the base system memory address of the
121                                 allocated range.
122   @param  DeviceAddress         The resulting map address for the bus master PCI controller to use to
123                                 access the hosts HostAddress.
124   @param  Mapping               A resulting value to pass to Unmap().
125 
126   @retval EFI_SUCCESS           The requested memory pages were allocated.
127   @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal attribute bits are
128                                 MEMORY_WRITE_COMBINE and MEMORY_CACHED.
129   @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
130   @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
131 
132 **/
133 EFI_STATUS
IoMmuAllocateBuffer(IN UINTN Pages,OUT VOID ** HostAddress,OUT EFI_PHYSICAL_ADDRESS * DeviceAddress,OUT VOID ** Mapping)134 IoMmuAllocateBuffer (
135   IN UINTN                  Pages,
136   OUT VOID                  **HostAddress,
137   OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,
138   OUT VOID                  **Mapping
139   )
140 {
141   EFI_STATUS            Status;
142   UINTN                 NumberOfBytes;
143   EFI_PHYSICAL_ADDRESS  HostPhyAddress;
144 
145   *HostAddress = NULL;
146   *DeviceAddress = 0;
147 
148   if (mIoMmu != NULL) {
149     Status = mIoMmu->AllocateBuffer (
150                        mIoMmu,
151                        EfiBootServicesData,
152                        Pages,
153                        HostAddress,
154                        0
155                        );
156     if (EFI_ERROR (Status)) {
157       return EFI_OUT_OF_RESOURCES;
158     }
159 
160     NumberOfBytes = EFI_PAGES_TO_SIZE(Pages);
161     Status = mIoMmu->Map (
162                        mIoMmu,
163                        EdkiiIoMmuOperationBusMasterCommonBuffer,
164                        *HostAddress,
165                        &NumberOfBytes,
166                        DeviceAddress,
167                        Mapping
168                        );
169     if (EFI_ERROR (Status)) {
170       return EFI_OUT_OF_RESOURCES;
171     }
172     Status = mIoMmu->SetAttribute (
173                        mIoMmu,
174                        *Mapping,
175                        EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE
176                        );
177     if (EFI_ERROR (Status)) {
178       return Status;
179     }
180   } else {
181     Status = PeiServicesAllocatePages (
182                EfiBootServicesData,
183                Pages,
184                &HostPhyAddress
185                );
186     if (EFI_ERROR (Status)) {
187       return EFI_OUT_OF_RESOURCES;
188     }
189     *HostAddress = (VOID *)(UINTN)HostPhyAddress;
190     *DeviceAddress = HostPhyAddress;
191     *Mapping = NULL;
192   }
193   return Status;
194 }
195 
196 /**
197   Frees memory that was allocated with AllocateBuffer().
198 
199   @param  Pages                 The number of pages to free.
200   @param  HostAddress           The base system memory address of the allocated range.
201   @param  Mapping               The mapping value returned from Map().
202 
203   @retval EFI_SUCCESS           The requested memory pages were freed.
204   @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
205                                 was not allocated with AllocateBuffer().
206 
207 **/
208 EFI_STATUS
IoMmuFreeBuffer(IN UINTN Pages,IN VOID * HostAddress,IN VOID * Mapping)209 IoMmuFreeBuffer (
210   IN UINTN                  Pages,
211   IN VOID                   *HostAddress,
212   IN VOID                   *Mapping
213   )
214 {
215   EFI_STATUS  Status;
216 
217   if (mIoMmu != NULL) {
218     Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0);
219     Status = mIoMmu->Unmap (mIoMmu, Mapping);
220     Status = mIoMmu->FreeBuffer (mIoMmu, Pages, HostAddress);
221   } else {
222     Status = EFI_SUCCESS;
223   }
224   return Status;
225 }
226 
227 /**
228   Initialize IOMMU.
229 **/
230 VOID
IoMmuInit(VOID)231 IoMmuInit (
232   VOID
233   )
234 {
235   PeiServicesLocatePpi (
236     &gEdkiiIoMmuPpiGuid,
237     0,
238     NULL,
239     (VOID **)&mIoMmu
240     );
241 }
242 
243