1 /** @file 2 Produces the SMM CPU I/O Protocol. 3 4 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR> 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 "CpuIo2Smm.h" 16 17 // 18 // Handle for the SMM CPU I/O Protocol 19 // 20 EFI_HANDLE mHandle = NULL; 21 22 // 23 // SMM CPU I/O Protocol instance 24 // 25 EFI_SMM_CPU_IO2_PROTOCOL mSmmCpuIo2 = { 26 { 27 CpuMemoryServiceRead, 28 CpuMemoryServiceWrite 29 }, 30 { 31 CpuIoServiceRead, 32 CpuIoServiceWrite 33 } 34 }; 35 36 // 37 // Lookup table for increment values based on transfer widths 38 // 39 UINT8 mStride[] = { 40 1, // SMM_IO_UINT8 41 2, // SMM_IO_UINT16 42 4, // SMM_IO_UINT32 43 8 // SMM_IO_UINT64 44 }; 45 46 /** 47 Check parameters to a SMM CPU I/O Protocol service request. 48 49 @param[in] MmioOperation TRUE for an MMIO operation, FALSE for I/O Port operation. 50 @param[in] Width Signifies the width of the I/O operations. 51 @param[in] Address The base address of the I/O operations. The caller is 52 responsible for aligning the Address if required. 53 @param[in] Count The number of I/O operations to perform. 54 @param[in] Buffer For read operations, the destination buffer to store 55 the results. For write operations, the source buffer 56 from which to write data. 57 58 @retval EFI_SUCCESS The data was read from or written to the device. 59 @retval EFI_UNSUPPORTED The Address is not valid for this system. 60 @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. 61 62 **/ 63 EFI_STATUS 64 CpuIoCheckParameter ( 65 IN BOOLEAN MmioOperation, 66 IN EFI_SMM_IO_WIDTH Width, 67 IN UINT64 Address, 68 IN UINTN Count, 69 IN VOID *Buffer 70 ) 71 { 72 UINT64 MaxCount; 73 UINT64 Limit; 74 75 // 76 // Check to see if Buffer is NULL 77 // 78 if (Buffer == NULL) { 79 return EFI_INVALID_PARAMETER; 80 } 81 82 // 83 // Check to see if Width is in the valid range 84 // 85 if ((UINT32)Width > SMM_IO_UINT64) { 86 return EFI_INVALID_PARAMETER; 87 } 88 89 // 90 // Check to see if Width is in the valid range for I/O Port operations 91 // 92 if (!MmioOperation && (Width == SMM_IO_UINT64)) { 93 return EFI_INVALID_PARAMETER; 94 } 95 96 // 97 // Check to see if any address associated with this transfer exceeds the maximum 98 // allowed address. The maximum address implied by the parameters passed in is 99 // Address + Size * Count. If the following condition is met, then the transfer 100 // is not supported. 101 // 102 // Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1 103 // 104 // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count 105 // can also be the maximum integer value supported by the CPU, this range 106 // check must be adjusted to avoid all overflow conditions. 107 // 108 // The following form of the range check is equivalent but assumes that 109 // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1). 110 // 111 Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS); 112 if (Count == 0) { 113 if (Address > Limit) { 114 return EFI_UNSUPPORTED; 115 } 116 } else { 117 MaxCount = RShiftU64 (Limit, Width); 118 if (MaxCount < (Count - 1)) { 119 return EFI_UNSUPPORTED; 120 } 121 if (Address > LShiftU64 (MaxCount - Count + 1, Width)) { 122 return EFI_UNSUPPORTED; 123 } 124 } 125 126 // 127 // Check to see if Address is aligned 128 // 129 if ((Address & (UINT64)(mStride[Width] - 1)) != 0) { 130 return EFI_UNSUPPORTED; 131 } 132 133 return EFI_SUCCESS; 134 } 135 136 /** 137 Reads memory-mapped registers. 138 139 The I/O operations are carried out exactly as requested. The caller is 140 responsible for any alignment and I/O width issues that the bus, device, 141 platform, or type of I/O might require. 142 143 @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. 144 @param[in] Width Signifies the width of the I/O operations. 145 @param[in] Address The base address of the I/O operations. The caller is 146 responsible for aligning the Address if required. 147 @param[in] Count The number of I/O operations to perform. 148 @param[out] Buffer For read operations, the destination buffer to store 149 the results. For write operations, the source buffer 150 from which to write data. 151 152 @retval EFI_SUCCESS The data was read from or written to the device. 153 @retval EFI_UNSUPPORTED The Address is not valid for this system. 154 @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. 155 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a 156 lack of resources 157 158 **/ 159 EFI_STATUS 160 EFIAPI 161 CpuMemoryServiceRead ( 162 IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, 163 IN EFI_SMM_IO_WIDTH Width, 164 IN UINT64 Address, 165 IN UINTN Count, 166 OUT VOID *Buffer 167 ) 168 { 169 EFI_STATUS Status; 170 UINT8 Stride; 171 UINT8 *Uint8Buffer; 172 173 Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); 174 if (EFI_ERROR (Status)) { 175 return Status; 176 } 177 178 // 179 // Select loop based on the width of the transfer 180 // 181 Stride = mStride[Width]; 182 for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) { 183 if (Width == SMM_IO_UINT8) { 184 *Uint8Buffer = MmioRead8 ((UINTN)Address); 185 } else if (Width == SMM_IO_UINT16) { 186 *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address); 187 } else if (Width == SMM_IO_UINT32) { 188 *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address); 189 } else if (Width == SMM_IO_UINT64) { 190 *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address); 191 } 192 } 193 return EFI_SUCCESS; 194 } 195 196 /** 197 Writes memory-mapped registers. 198 199 The I/O operations are carried out exactly as requested. The caller is 200 responsible for any alignment and I/O width issues that the bus, device, 201 platform, or type of I/O might require. 202 203 @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. 204 @param[in] Width Signifies the width of the I/O operations. 205 @param[in] Address The base address of the I/O operations. The caller is 206 responsible for aligning the Address if required. 207 @param[in] Count The number of I/O operations to perform. 208 @param[in] Buffer For read operations, the destination buffer to store 209 the results. For write operations, the source buffer 210 from which to write data. 211 212 @retval EFI_SUCCESS The data was read from or written to the device. 213 @retval EFI_UNSUPPORTED The Address is not valid for this system. 214 @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. 215 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a 216 lack of resources 217 218 **/ 219 EFI_STATUS 220 EFIAPI 221 CpuMemoryServiceWrite ( 222 IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, 223 IN EFI_SMM_IO_WIDTH Width, 224 IN UINT64 Address, 225 IN UINTN Count, 226 IN VOID *Buffer 227 ) 228 { 229 EFI_STATUS Status; 230 UINT8 Stride; 231 UINT8 *Uint8Buffer; 232 233 Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); 234 if (EFI_ERROR (Status)) { 235 return Status; 236 } 237 238 // 239 // Select loop based on the width of the transfer 240 // 241 Stride = mStride[Width]; 242 for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) { 243 if (Width == SMM_IO_UINT8) { 244 MmioWrite8 ((UINTN)Address, *Uint8Buffer); 245 } else if (Width == SMM_IO_UINT16) { 246 MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); 247 } else if (Width == SMM_IO_UINT32) { 248 MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); 249 } else if (Width == SMM_IO_UINT64) { 250 MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer)); 251 } 252 } 253 return EFI_SUCCESS; 254 } 255 256 /** 257 Reads I/O registers. 258 259 The I/O operations are carried out exactly as requested. The caller is 260 responsible for any alignment and I/O width issues that the bus, device, 261 platform, or type of I/O might require. 262 263 @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. 264 @param[in] Width Signifies the width of the I/O operations. 265 @param[in] Address The base address of the I/O operations. The caller is 266 responsible for aligning the Address if required. 267 @param[in] Count The number of I/O operations to perform. 268 @param[out] Buffer For read operations, the destination buffer to store 269 the results. For write operations, the source buffer 270 from which to write data. 271 272 @retval EFI_SUCCESS The data was read from or written to the device. 273 @retval EFI_UNSUPPORTED The Address is not valid for this system. 274 @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. 275 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a 276 lack of resources 277 278 **/ 279 EFI_STATUS 280 EFIAPI 281 CpuIoServiceRead ( 282 IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, 283 IN EFI_SMM_IO_WIDTH Width, 284 IN UINT64 Address, 285 IN UINTN Count, 286 OUT VOID *Buffer 287 ) 288 { 289 EFI_STATUS Status; 290 UINT8 Stride; 291 UINT8 *Uint8Buffer; 292 293 Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); 294 if (EFI_ERROR (Status)) { 295 return Status; 296 } 297 298 // 299 // Select loop based on the width of the transfer 300 // 301 Stride = mStride[Width]; 302 for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) { 303 if (Width == SMM_IO_UINT8) { 304 *Uint8Buffer = IoRead8 ((UINTN)Address); 305 } else if (Width == SMM_IO_UINT16) { 306 *((UINT16 *)Uint8Buffer) = IoRead16 ((UINTN)Address); 307 } else if (Width == SMM_IO_UINT32) { 308 *((UINT32 *)Uint8Buffer) = IoRead32 ((UINTN)Address); 309 } 310 } 311 312 return EFI_SUCCESS; 313 } 314 315 /** 316 Write I/O registers. 317 318 The I/O operations are carried out exactly as requested. The caller is 319 responsible for any alignment and I/O width issues that the bus, device, 320 platform, or type of I/O might require. 321 322 @param[in] This The EFI_SMM_CPU_IO2_PROTOCOL instance. 323 @param[in] Width Signifies the width of the I/O operations. 324 @param[in] Address The base address of the I/O operations. The caller is 325 responsible for aligning the Address if required. 326 @param[in] Count The number of I/O operations to perform. 327 @param[in] Buffer For read operations, the destination buffer to store 328 the results. For write operations, the source buffer 329 from which to write data. 330 331 @retval EFI_SUCCESS The data was read from or written to the device. 332 @retval EFI_UNSUPPORTED The Address is not valid for this system. 333 @retval EFI_INVALID_PARAMETER Width or Count, or both, were invalid. 334 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a 335 lack of resources 336 337 **/ 338 EFI_STATUS 339 EFIAPI 340 CpuIoServiceWrite ( 341 IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This, 342 IN EFI_SMM_IO_WIDTH Width, 343 IN UINT64 Address, 344 IN UINTN Count, 345 IN VOID *Buffer 346 ) 347 { 348 EFI_STATUS Status; 349 UINT8 Stride; 350 UINT8 *Uint8Buffer; 351 352 // 353 // Make sure the parameters are valid 354 // 355 Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); 356 if (EFI_ERROR (Status)) { 357 return Status; 358 } 359 360 // 361 // Select loop based on the width of the transfer 362 // 363 Stride = mStride[Width]; 364 for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) { 365 if (Width == SMM_IO_UINT8) { 366 IoWrite8 ((UINTN)Address, *Uint8Buffer); 367 } else if (Width == SMM_IO_UINT16) { 368 IoWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); 369 } else if (Width == SMM_IO_UINT32) { 370 IoWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); 371 } 372 } 373 374 return EFI_SUCCESS; 375 } 376 377 /** 378 The module Entry Point SmmCpuIoProtocol driver 379 380 @param[in] ImageHandle The firmware allocated handle for the EFI image. 381 @param[in] SystemTable A pointer to the EFI System Table. 382 383 @retval EFI_SUCCESS The entry point is executed successfully. 384 @retval Other Some error occurs when executing this entry point. 385 386 **/ 387 EFI_STATUS 388 EFIAPI 389 SmmCpuIo2Initialize ( 390 IN EFI_HANDLE ImageHandle, 391 IN EFI_SYSTEM_TABLE *SystemTable 392 ) 393 { 394 EFI_STATUS Status; 395 396 // 397 // Copy the SMM CPU I/O Protocol instance into the System Management System Table 398 // 399 CopyMem (&gSmst->SmmIo, &mSmmCpuIo2, sizeof (mSmmCpuIo2)); 400 401 // 402 // Install the SMM CPU I/O Protocol into the SMM protocol database 403 // 404 Status = gSmst->SmmInstallProtocolInterface ( 405 &mHandle, 406 &gEfiSmmCpuIo2ProtocolGuid, 407 EFI_NATIVE_INTERFACE, 408 &mSmmCpuIo2 409 ); 410 ASSERT_EFI_ERROR (Status); 411 412 return Status; 413 } 414