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 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 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 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 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 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