1 /** @file
2 *
3 *  Copyright (c) 2013, ARM Limited. All rights reserved.
4 *  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
5 *
6 *  SPDX-License-Identifier: BSD-2-Clause-Patent
7 *
8 **/
9 
10 #include "CpuDxe.h"
11 
12 /**
13   Searches memory descriptors covered by given memory range.
14 
15   This function searches into the Gcd Memory Space for descriptors
16   (from StartIndex to EndIndex) that contains the memory range
17   specified by BaseAddress and Length.
18 
19   @param  MemorySpaceMap       Gcd Memory Space Map as array.
20   @param  NumberOfDescriptors  Number of descriptors in map.
21   @param  BaseAddress          BaseAddress for the requested range.
22   @param  Length               Length for the requested range.
23   @param  StartIndex           Start index into the Gcd Memory Space Map.
24   @param  EndIndex             End index into the Gcd Memory Space Map.
25 
26   @retval EFI_SUCCESS          Search successfully.
27   @retval EFI_NOT_FOUND        The requested descriptors does not exist.
28 
29 **/
30 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)31 SearchGcdMemorySpaces (
32   IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap,
33   IN UINTN                               NumberOfDescriptors,
34   IN EFI_PHYSICAL_ADDRESS                BaseAddress,
35   IN UINT64                              Length,
36   OUT UINTN                             *StartIndex,
37   OUT UINTN                             *EndIndex
38   )
39 {
40   UINTN           Index;
41 
42   *StartIndex = 0;
43   *EndIndex   = 0;
44   for (Index = 0; Index < NumberOfDescriptors; Index++) {
45     if ((BaseAddress >= MemorySpaceMap[Index].BaseAddress) &&
46         (BaseAddress < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) {
47       *StartIndex = Index;
48     }
49     if (((BaseAddress + Length - 1) >= MemorySpaceMap[Index].BaseAddress) &&
50         ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) {
51       *EndIndex = Index;
52       return EFI_SUCCESS;
53     }
54   }
55   return EFI_NOT_FOUND;
56 }
57 
58 
59 /**
60   Sets the attributes for a specified range in Gcd Memory Space Map.
61 
62   This function sets the attributes for a specified range in
63   Gcd Memory Space Map.
64 
65   @param  MemorySpaceMap       Gcd Memory Space Map as array
66   @param  NumberOfDescriptors  Number of descriptors in map
67   @param  BaseAddress          BaseAddress for the range
68   @param  Length               Length for the range
69   @param  Attributes           Attributes to set
70 
71   @retval EFI_SUCCESS          Memory attributes set successfully
72   @retval EFI_NOT_FOUND        The specified range does not exist in Gcd Memory Space
73 
74 **/
75 EFI_STATUS
SetGcdMemorySpaceAttributes(IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR * MemorySpaceMap,IN UINTN NumberOfDescriptors,IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes)76 SetGcdMemorySpaceAttributes (
77   IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR    *MemorySpaceMap,
78   IN UINTN                               NumberOfDescriptors,
79   IN EFI_PHYSICAL_ADDRESS                BaseAddress,
80   IN UINT64                              Length,
81   IN UINT64                              Attributes
82   )
83 {
84   EFI_STATUS            Status;
85   UINTN                 Index;
86   UINTN                 StartIndex;
87   UINTN                 EndIndex;
88   EFI_PHYSICAL_ADDRESS  RegionStart;
89   UINT64                RegionLength;
90 
91   DEBUG ((DEBUG_GCD, "SetGcdMemorySpaceAttributes[0x%lX; 0x%lX] = 0x%lX\n",
92       BaseAddress, BaseAddress + Length, Attributes));
93 
94   // We do not support a smaller granularity than 4KB on ARM Architecture
95   if ((Length & EFI_PAGE_MASK) != 0) {
96     DEBUG ((DEBUG_WARN,
97             "Warning: We do not support smaller granularity than 4KB on ARM Architecture (passed length: 0x%lX).\n",
98             Length));
99   }
100 
101   //
102   // Get all memory descriptors covered by the memory range
103   //
104   Status = SearchGcdMemorySpaces (
105              MemorySpaceMap,
106              NumberOfDescriptors,
107              BaseAddress,
108              Length,
109              &StartIndex,
110              &EndIndex
111              );
112   if (EFI_ERROR (Status)) {
113     return Status;
114   }
115 
116   //
117   // Go through all related descriptors and set attributes accordingly
118   //
119   for (Index = StartIndex; Index <= EndIndex; Index++) {
120     if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
121       continue;
122     }
123     //
124     // Calculate the start and end address of the overlapping range
125     //
126     if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) {
127       RegionStart = BaseAddress;
128     } else {
129       RegionStart = MemorySpaceMap[Index].BaseAddress;
130     }
131     if ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length)) {
132       RegionLength = BaseAddress + Length - RegionStart;
133     } else {
134       RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart;
135     }
136     //
137     // Set memory attributes according to MTRR attribute and the original attribute of descriptor
138     //
139     gDS->SetMemorySpaceAttributes (
140            RegionStart,
141            RegionLength,
142            (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes)
143            );
144   }
145 
146   return EFI_SUCCESS;
147 }
148 
149 /**
150   This function modifies the attributes for the memory region specified by BaseAddress and
151   Length from their current attributes to the attributes specified by Attributes.
152 
153   @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
154   @param  BaseAddress      The physical address that is the start address of a memory region.
155   @param  Length           The size in bytes of the memory region.
156   @param  Attributes       The bit mask of attributes to set for the memory region.
157 
158   @retval EFI_SUCCESS           The attributes were set for the memory region.
159   @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
160                                 BaseAddress and Length cannot be modified.
161   @retval EFI_INVALID_PARAMETER Length is zero.
162   @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
163                                 the memory resource range.
164   @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
165                                 resource range specified by BaseAddress and Length.
166                                 The bit mask of attributes is not support for the memory resource
167                                 range specified by BaseAddress and Length.
168 
169 **/
170 EFI_STATUS
171 EFIAPI
CpuSetMemoryAttributes(IN EFI_CPU_ARCH_PROTOCOL * This,IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 EfiAttributes)172 CpuSetMemoryAttributes (
173   IN EFI_CPU_ARCH_PROTOCOL    *This,
174   IN EFI_PHYSICAL_ADDRESS      BaseAddress,
175   IN UINT64                    Length,
176   IN UINT64                    EfiAttributes
177   )
178 {
179   EFI_STATUS  Status;
180   UINTN       ArmAttributes;
181   UINTN       RegionBaseAddress;
182   UINTN       RegionLength;
183   UINTN       RegionArmAttributes;
184 
185   if (mIsFlushingGCD) {
186     return EFI_SUCCESS;
187   }
188 
189   if ((BaseAddress & (SIZE_4KB - 1)) != 0) {
190     // Minimum granularity is SIZE_4KB (4KB on ARM)
191     DEBUG ((DEBUG_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes));
192     return EFI_UNSUPPORTED;
193   }
194 
195   // Convert the 'Attribute' into ARM Attribute
196   ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes);
197 
198   // Get the region starting from 'BaseAddress' and its 'Attribute'
199   RegionBaseAddress = BaseAddress;
200   Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes);
201 
202   // Data & Instruction Caches are flushed when we set new memory attributes.
203   // So, we only set the attributes if the new region is different.
204   if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) ||
205       ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
206   {
207     return ArmSetMemoryAttributes (BaseAddress, Length, EfiAttributes);
208   } else {
209     return EFI_SUCCESS;
210   }
211 }
212