]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c
54d9b016333107be81fa07c6fb01996bb0652c07
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / CpuMmuCommon.c
1 /** @file
2 *
3 * Copyright (c) 2013, ARM Limited. All rights reserved.
4 * Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
5 *
6 * This program and the accompanying materials
7 * are licensed and made available under the terms and conditions of the BSD License
8 * which accompanies this distribution. The full text of the license may be found at
9 * http://opensource.org/licenses/bsd-license.php
10 *
11 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 *
14 **/
15
16 #include "CpuDxe.h"
17
18 /**
19 Searches memory descriptors covered by given memory range.
20
21 This function searches into the Gcd Memory Space for descriptors
22 (from StartIndex to EndIndex) that contains the memory range
23 specified by BaseAddress and Length.
24
25 @param MemorySpaceMap Gcd Memory Space Map as array.
26 @param NumberOfDescriptors Number of descriptors in map.
27 @param BaseAddress BaseAddress for the requested range.
28 @param Length Length for the requested range.
29 @param StartIndex Start index into the Gcd Memory Space Map.
30 @param EndIndex End index into the Gcd Memory Space Map.
31
32 @retval EFI_SUCCESS Search successfully.
33 @retval EFI_NOT_FOUND The requested descriptors does not exist.
34
35 **/
36 EFI_STATUS
37 SearchGcdMemorySpaces (
38 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
39 IN UINTN NumberOfDescriptors,
40 IN EFI_PHYSICAL_ADDRESS BaseAddress,
41 IN UINT64 Length,
42 OUT UINTN *StartIndex,
43 OUT UINTN *EndIndex
44 )
45 {
46 UINTN Index;
47
48 *StartIndex = 0;
49 *EndIndex = 0;
50 for (Index = 0; Index < NumberOfDescriptors; Index++) {
51 if ((BaseAddress >= MemorySpaceMap[Index].BaseAddress) &&
52 (BaseAddress < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) {
53 *StartIndex = Index;
54 }
55 if (((BaseAddress + Length - 1) >= MemorySpaceMap[Index].BaseAddress) &&
56 ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) {
57 *EndIndex = Index;
58 return EFI_SUCCESS;
59 }
60 }
61 return EFI_NOT_FOUND;
62 }
63
64
65 /**
66 Sets the attributes for a specified range in Gcd Memory Space Map.
67
68 This function sets the attributes for a specified range in
69 Gcd Memory Space Map.
70
71 @param MemorySpaceMap Gcd Memory Space Map as array
72 @param NumberOfDescriptors Number of descriptors in map
73 @param BaseAddress BaseAddress for the range
74 @param Length Length for the range
75 @param Attributes Attributes to set
76
77 @retval EFI_SUCCESS Memory attributes set successfully
78 @retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space
79
80 **/
81 EFI_STATUS
82 SetGcdMemorySpaceAttributes (
83 IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap,
84 IN UINTN NumberOfDescriptors,
85 IN EFI_PHYSICAL_ADDRESS BaseAddress,
86 IN UINT64 Length,
87 IN UINT64 Attributes
88 )
89 {
90 EFI_STATUS Status;
91 UINTN Index;
92 UINTN StartIndex;
93 UINTN EndIndex;
94 EFI_PHYSICAL_ADDRESS RegionStart;
95 UINT64 RegionLength;
96
97 DEBUG ((DEBUG_GCD, "SetGcdMemorySpaceAttributes[0x%lX; 0x%lX] = 0x%lX\n",
98 BaseAddress, BaseAddress + Length, Attributes));
99
100 // We do not support a smaller granularity than 4KB on ARM Architecture
101 if ((Length & EFI_PAGE_MASK) != 0) {
102 DEBUG ((DEBUG_WARN,
103 "Warning: We do not support smaller granularity than 4KB on ARM Architecture (passed length: 0x%lX).\n",
104 Length));
105 }
106
107 //
108 // Get all memory descriptors covered by the memory range
109 //
110 Status = SearchGcdMemorySpaces (
111 MemorySpaceMap,
112 NumberOfDescriptors,
113 BaseAddress,
114 Length,
115 &StartIndex,
116 &EndIndex
117 );
118 if (EFI_ERROR (Status)) {
119 return Status;
120 }
121
122 //
123 // Go through all related descriptors and set attributes accordingly
124 //
125 for (Index = StartIndex; Index <= EndIndex; Index++) {
126 if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
127 continue;
128 }
129 //
130 // Calculate the start and end address of the overlapping range
131 //
132 if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {
133 RegionStart = BaseAddress;
134 } else {
135 RegionStart = MemorySpaceMap[Index].BaseAddress;
136 }
137 if ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length)) {
138 RegionLength = BaseAddress + Length - RegionStart;
139 } else {
140 RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;
141 }
142 //
143 // Set memory attributes according to MTRR attribute and the original attribute of descriptor
144 //
145 gDS->SetMemorySpaceAttributes (
146 RegionStart,
147 RegionLength,
148 (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)
149 );
150 }
151
152 return EFI_SUCCESS;
153 }
154
155 /**
156 This function modifies the attributes for the memory region specified by BaseAddress and
157 Length from their current attributes to the attributes specified by Attributes.
158
159 @param This The EFI_CPU_ARCH_PROTOCOL instance.
160 @param BaseAddress The physical address that is the start address of a memory region.
161 @param Length The size in bytes of the memory region.
162 @param Attributes The bit mask of attributes to set for the memory region.
163
164 @retval EFI_SUCCESS The attributes were set for the memory region.
165 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
166 BaseAddress and Length cannot be modified.
167 @retval EFI_INVALID_PARAMETER Length is zero.
168 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
169 the memory resource range.
170 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
171 resource range specified by BaseAddress and Length.
172 The bit mask of attributes is not support for the memory resource
173 range specified by BaseAddress and Length.
174
175 **/
176 EFI_STATUS
177 EFIAPI
178 CpuSetMemoryAttributes (
179 IN EFI_CPU_ARCH_PROTOCOL *This,
180 IN EFI_PHYSICAL_ADDRESS BaseAddress,
181 IN UINT64 Length,
182 IN UINT64 EfiAttributes
183 )
184 {
185 EFI_STATUS Status;
186 UINTN ArmAttributes;
187 UINTN RegionBaseAddress;
188 UINTN RegionLength;
189 UINTN RegionArmAttributes;
190
191 if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
192 // Minimum granularity is SIZE_4KB (4KB on ARM)
193 DEBUG ((EFI_D_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum ganularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes));
194 return EFI_UNSUPPORTED;
195 }
196
197 // Convert the 'Attribute' into ARM Attribute
198 ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes);
199
200 // Get the region starting from 'BaseAddress' and its 'Attribute'
201 RegionBaseAddress = BaseAddress;
202 Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes);
203
204 // Data & Instruction Caches are flushed when we set new memory attributes.
205 // So, we only set the attributes if the new region is different.
206 if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) ||
207 ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
208 {
209 return SetMemoryAttributes (BaseAddress, Length, EfiAttributes, 0);
210 } else {
211 return EFI_SUCCESS;
212 }
213 }
214
215 EFI_STATUS
216 EFIAPI
217 CpuConvertPagesToUncachedVirtualAddress (
218 IN VIRTUAL_UNCACHED_PAGES_PROTOCOL *This,
219 IN EFI_PHYSICAL_ADDRESS Address,
220 IN UINTN Length,
221 IN EFI_PHYSICAL_ADDRESS VirtualMask,
222 OUT UINT64 *Attributes OPTIONAL
223 )
224 {
225 EFI_STATUS Status;
226 EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
227
228 if (Attributes != NULL) {
229 Status = gDS->GetMemorySpaceDescriptor (Address, &GcdDescriptor);
230 if (!EFI_ERROR (Status)) {
231 *Attributes = GcdDescriptor.Attributes;
232 }
233 }
234
235 //
236 // Make this address range page fault if accessed. If it is a DMA buffer than this would
237 // be the PCI address. Code should always use the CPU address, and we will or in VirtualMask
238 // to that address.
239 //
240 Status = SetMemoryAttributes (Address, Length, EFI_MEMORY_RO, 0);
241 if (!EFI_ERROR (Status)) {
242 Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_UC, VirtualMask);
243 }
244
245 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "CpuConvertPagesToUncachedVirtualAddress()\n Unmapped 0x%08lx Mapped 0x%08lx 0x%x bytes\n", Address, Address | VirtualMask, Length));
246
247 return Status;
248 }
249
250
251 EFI_STATUS
252 EFIAPI
253 CpuReconvertPages (
254 IN VIRTUAL_UNCACHED_PAGES_PROTOCOL *This,
255 IN EFI_PHYSICAL_ADDRESS Address,
256 IN UINTN Length,
257 IN EFI_PHYSICAL_ADDRESS VirtualMask,
258 IN UINT64 Attributes
259 )
260 {
261 EFI_STATUS Status;
262
263 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "CpuReconvertPages(%lx, %x, %lx, %lx)\n", Address, Length, VirtualMask, Attributes));
264
265 //
266 // Unmap the aliased Address
267 //
268 Status = SetMemoryAttributes (Address | VirtualMask, Length, EFI_MEMORY_RO, 0);
269 if (!EFI_ERROR (Status)) {
270 //
271 // Restore atttributes
272 //
273 Status = SetMemoryAttributes (Address, Length, Attributes, 0);
274 }
275
276 return Status;
277 }
278
279
280 VIRTUAL_UNCACHED_PAGES_PROTOCOL gVirtualUncachedPages = {
281 CpuConvertPagesToUncachedVirtualAddress,
282 CpuReconvertPages
283 };