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